Merge "Move codes generating html file from xml files to SettingsLib (2/2)"
diff --git a/Android.mk b/Android.mk
index d4d2baa..7ca8358 100644
--- a/Android.mk
+++ b/Android.mk
@@ -46,6 +46,7 @@
 	frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \
+	frameworks/base/telephony/java/android/telephony/data/DataCallResponse.aidl \
 	frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3eb8667..f56f1a1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4995,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();
@@ -6323,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);
@@ -9973,6 +9974,7 @@
   }
 
   public class QuickViewConstants {
+    field public static final java.lang.String FEATURE_DELETE = "android:delete";
     field public static final java.lang.String FEATURE_DOWNLOAD = "android:download";
     field public static final java.lang.String FEATURE_EDIT = "android:edit";
     field public static final java.lang.String FEATURE_PRINT = "android:print";
@@ -11260,6 +11262,8 @@
 package android.content.pm.crossprofile {
 
   public class CrossProfileApps {
+    method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle);
+    method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
     method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
@@ -37147,6 +37151,11 @@
 
 package android.security {
 
+  public final class AttestedKeyPair {
+    method public java.util.List<java.security.cert.Certificate> getAttestationRecord();
+    method public java.security.KeyPair getKeyPair();
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1d206c17..33fa246 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4177,6 +4177,25 @@
 
 package android.telephony.data {
 
+  public final class DataCallResponse implements android.os.Parcelable {
+    ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.telephony.data.InterfaceAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
+    ctor public DataCallResponse(android.os.Parcel);
+    method public int describeContents();
+    method public int getActive();
+    method public java.util.List<android.telephony.data.InterfaceAddress> getAddresses();
+    method public int getCallId();
+    method public java.util.List<java.net.InetAddress> getDnses();
+    method public java.util.List<java.net.InetAddress> getGateways();
+    method public java.lang.String getIfname();
+    method public int getMtu();
+    method public java.util.List<java.lang.String> getPcscfs();
+    method public int getStatus();
+    method public int getSuggestedRetryTime();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
+  }
+
   public final class DataProfile implements android.os.Parcelable {
     ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
     ctor public DataProfile(android.os.Parcel);
@@ -4206,6 +4225,17 @@
     field public static final int TYPE_COMMON = 0; // 0x0
   }
 
+  public final class InterfaceAddress implements android.os.Parcelable {
+    ctor public InterfaceAddress(java.net.InetAddress, int);
+    ctor public InterfaceAddress(java.lang.String, int) throws java.net.UnknownHostException;
+    ctor public InterfaceAddress(android.os.Parcel);
+    method public int describeContents();
+    method public java.net.InetAddress getAddress();
+    method public int getNetworkPrefixLength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+  }
+
 }
 
 package android.telephony.ims {
diff --git a/api/test-current.txt b/api/test-current.txt
index 5cbefb2..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();
   }
@@ -959,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/atoms.proto b/cmds/statsd/src/atoms.proto
index 968d08f..c81fc1d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -103,7 +103,11 @@
  * resulted in a particular bit of work being done.
  */
 message WorkSource {
-    // TODO
+    // The uid for a given element in the attribution chain.
+    repeated int32 uid = 1;
+    // The (optional) string tag for an element in the attribution chain. If the
+    // element has no tag, it is encoded as an empty string.
+    repeated string tag = 2;
 }
 
 /*
@@ -413,8 +417,10 @@
     optional string tag = 3;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        RELEASE = 0;
+        ACQUIRE = 1;
+        CHANGE_RELEASE = 2;
+        CHANGE_ACQUIRE = 3;
     }
     optional State state = 4;
 }
@@ -1066,4 +1072,4 @@
     optional uint64 controller_rx_time_ms = 9;
     // product of current(mA), voltage(V) and time(ms)
     optional uint64 energy_used = 10;
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto
index 58e225a..18b2144 100644
--- a/cmds/statsd/src/atoms_copy.proto
+++ b/cmds/statsd/src/atoms_copy.proto
@@ -99,7 +99,11 @@
  * resulted in a particular bit of work being done.
  */
 message WorkSource {
-    // TODO
+    // The uid for a given element in the attribution chain.
+    repeated int32 uid = 1;
+    // The (optional) string tag for an element in the attribution chain. If the
+    // element has no tag, it is encoded as an empty string.
+    repeated string tag = 2;
 }
 
 /*
@@ -404,8 +408,10 @@
     optional string tag = 3;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        RELEASE = 0;
+        ACQUIRE = 1;
+        CHANGE_RELEASE = 2;
+        CHANGE_ACQUIRE = 3;
     }
     optional State state = 4;
 }
@@ -907,4 +913,4 @@
     optional uint64 uid = 1;
     optional uint64 freq_idx = 2;
     optional uint64 time_ms = 3;
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/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/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 6a76c55..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);
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/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 1fc4d42..943becb 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -64,7 +64,7 @@
 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) {
@@ -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;
             }
         }
@@ -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 ca63b03..c9654af 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -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];
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/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml
index d74c707..2bf8ca9 100644
--- a/cmds/statsd/tools/loadtest/AndroidManifest.xml
+++ b/cmds/statsd/tools/loadtest/AndroidManifest.xml
@@ -39,5 +39,6 @@
     </activity>
     <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
     <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
+    <receiver android:name=".PerfData$PerfAlarmReceiver"/>
   </application>
 </manifest>
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
index 82964ab..1e28f67 100644
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
@@ -166,12 +166,6 @@
             android:layout_height="wrap_content"
             android:text="@string/display_output"
             android:textSize="30dp"/>
-        <Button
-            android:id="@+id/display_perf"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/display_perf"
-            android:textSize="30dp"/>
 
         <Space
             android:layout_width="1dp"
@@ -179,6 +173,7 @@
 
         <TextView
             android:id="@+id/report_text"
+            android:gravity="center"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
index 9913503..cb38298 100644
--- a/cmds/statsd/tools/loadtest/res/values/strings.xml
+++ b/cmds/statsd/tools/loadtest/res/values/strings.xml
@@ -21,7 +21,6 @@
     <string name="bucket_label">bucket size (mins):&#160;</string>
     <string name="burst_label">burst:&#160;</string>
     <string name="display_output">Show metrics data</string>
-    <string name="display_perf">Show perf data</string>
     <string name="placebo">placebo</string>
     <string name="period_label">logging period (secs):&#160;</string>
     <string name="replication_label">metric replication:&#160;</string>
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
new file mode 100644
index 0000000..d2ff892
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Log;
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BatteryDataRecorder extends PerfDataRecorder {
+    private static final String TAG = "loadtest.BatteryDataRecorder";
+    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+
+    public BatteryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    @Override
+    public void startRecording(Context context) {
+        // Reset batterystats.
+        runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset");
+    }
+
+    @Override
+    public void onAlarm(Context context) {
+        // Nothing to do as for battery, the whole data is in the final dumpsys call.
+    }
+
+    @Override
+    public void stopRecording(Context context) {
+        StringBuilder sb = new StringBuilder();
+        // Don't use --checkin.
+        runDumpsysStats(context, DUMP_FILENAME, "batterystats");
+        readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb);
+        writeData(context, "battery_", "time,battery_level", sb);
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
index 96e6bef..203d97a 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
@@ -21,13 +21,13 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class BatteryStatsParser {
+public class BatteryStatsParser implements PerfParser {
 
     private static final Pattern LINE_PATTERN =
         Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
     private static final Pattern TIME_PATTERN =
         Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
-    private static final String TAG = "BatteryStatsParser";
+    private static final String TAG = "loadtest.BatteryStatsParser";
 
     private boolean mHistoryStarted;
     private boolean mHistoryEnded;
@@ -35,6 +35,7 @@
     public BatteryStatsParser() {
     }
 
+    @Override
     @Nullable
     public String parseLine(String line) {
         if (mHistoryEnded) {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index 8d20f97..0d890fb 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -18,13 +18,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
-import android.util.StatsLog;
 
 import com.android.internal.os.StatsdConfigProto.Bucket;
 import com.android.internal.os.StatsdConfigProto.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;
@@ -45,7 +44,7 @@
 public class ConfigFactory {
     public static final String CONFIG_NAME = "LOADTEST";
 
-    private static final String TAG = "ConfigFactory";
+    private static final String TAG = "loadtest.ConfigFactory";
 
     private final StatsdConfig mTemplate;
 
@@ -130,13 +129,13 @@
     }
 
     /**
-     * Creates {@link EventConditionLink}s that are identical to the one passed to this method,
+     * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
      * except that the names are appended with the provided suffix.
      */
-    private List<EventConditionLink> getLinks(
-        List<EventConditionLink> links, int suffix) {
-        List<EventConditionLink> newLinks = new ArrayList();
-        for (EventConditionLink link : links) {
+    private List<MetricConditionLink> getLinks(
+        List<MetricConditionLink> links, int suffix) {
+        List<MetricConditionLink> newLinks = new ArrayList();
+        for (MetricConditionLink link : links) {
             newLinks.add(link.toBuilder()
                 .setCondition(link.getCondition() + suffix)
                 .build());
@@ -156,7 +155,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -182,7 +181,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -203,7 +202,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -224,7 +223,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -245,7 +244,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index 3ae85a7..522dea6 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -65,8 +65,9 @@
 public class LoadtestActivity extends Activity {
 
     private static final String TAG = "StatsdLoadtest";
-    private static final String TYPE = "type";
-    private static final String ALARM = "push_alarm";
+    public static final String TYPE = "type";
+    private static final String PUSH_ALARM = "push_alarm";
+    public static final String PERF_ALARM = "perf_alarm";
     private static final String START = "start";
     private static final String STOP = "stop";
 
@@ -74,7 +75,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             Intent activityIntent = new Intent(context, LoadtestActivity.class);
-            activityIntent.putExtra(TYPE, ALARM);
+            activityIntent.putExtra(TYPE, PUSH_ALARM);
             context.startActivity(activityIntent);
          }
     }
@@ -105,6 +106,9 @@
     private TextView mReportText;
     private CheckBox mPlaceboCheckBox;
 
+    /** When the load test started. */
+    private long mStartedTimeMillis;
+
     /** For measuring perf data. */
     private PerfData mPerfData;
 
@@ -180,7 +184,7 @@
             @Override
             public void onClick(View view) {
                 if (mStarted) {
-                    stopLoadtest(true);
+                    stopLoadtest();
                 } else {
                     startLoadtest();
                 }
@@ -194,20 +198,11 @@
             }
         });
 
-        findViewById(R.id.display_perf).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mPerfData.publishData(LoadtestActivity.this, mPlacebo, mReplication, mBucketMins,
-                    mPeriodSecs, mBurst);
-            }
-        });
-
         mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
         mStatsManager = (StatsManager) getSystemService("stats");
         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mFactory = new ConfigFactory(this);
-        mPerfData = new PerfData();
-        stopLoadtest(false);
+        stopLoadtest();
         mReportText.setText("");
     }
 
@@ -218,14 +213,17 @@
             return;
         }
         switch (type) {
-            case ALARM:
-              onAlarm(intent);
-              break;
+            case PERF_ALARM:
+                onPerfAlarm();
+                break;
+            case PUSH_ALARM:
+                onAlarm();
+                break;
             case START:
                 startLoadtest();
                 break;
-          case STOP:
-                stopLoadtest(true);
+            case STOP:
+                stopLoadtest();
                 break;
             default:
                 throw new IllegalArgumentException("Unknown type: " + type);
@@ -235,12 +233,23 @@
     @Override
     public void onDestroy() {
         Log.d(TAG, "Destroying");
-        stopLoadtest(false);
+        mPerfData.onDestroy();
+        stopLoadtest();
         clearConfigs();
         super.onDestroy();
     }
 
-    private void onAlarm(Intent intent) {
+    private void onPerfAlarm() {
+        if (mPerfData != null) {
+            mPerfData.onAlarm(this);
+        }
+        // Piggy-back on that alarm to show the elapsed time.
+        long elapsedTimeMins = (long) Math.floor(
+            (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+        mReportText.setText("Loadtest in progress. Elapsed time = " + elapsedTimeMins + " min(s)");
+    }
+
+    private void onAlarm() {
         Log.d(TAG, "ON ALARM");
 
         // Set the next task.
@@ -259,7 +268,7 @@
     /** Schedules the next cycle of pushing atoms into logd. */
     private void scheduleNext() {
         Intent intent = new Intent(this, PusherAlarmReceiver.class);
-        intent.putExtra(TYPE, ALARM);
+        intent.putExtra(TYPE, PUSH_ALARM);
         mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
         long nextTime =  SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
         mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
@@ -271,7 +280,7 @@
         }
 
         // Clean up the state.
-        stopLoadtest(false);
+        stopLoadtest();
 
         // Prepare to push a sequence of atoms to logd.
         mPusher = new SequencePusher(mBurst, mPlacebo);
@@ -291,15 +300,17 @@
         // Log atoms.
         scheduleNext();
 
-        // Reset battery data.
-        mPerfData.resetData(this);
+        // Start tracking performance.
+        mPerfData = new PerfData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
+        mPerfData.startRecording(this);
 
-        mReportText.setText("");
+        mReportText.setText("Loadtest in progress.");
+        mStartedTimeMillis = SystemClock.elapsedRealtime();
 
         updateStarted(true);
     }
 
-    private synchronized void stopLoadtest(boolean publishPerfData) {
+    private synchronized void stopLoadtest() {
         if (mPushPendingIntent != null) {
             Log.d(TAG, "Canceling pre-existing push alarm");
             mAlarmMgr.cancel(mPushPendingIntent);
@@ -314,12 +325,17 @@
             mWakeLock.release();
             mWakeLock = null;
         }
-        fetchAndDisplayData();
+        if (mPerfData != null) {
+            mPerfData.stopRecording(this);
+            mPerfData.onDestroy();
+            mPerfData = null;
+        }
+
+        long elapsedTimeMins = (long) Math.floor(
+            (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+        mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
         clearConfigs();
         updateStarted(false);
-        if (publishPerfData) {
-            mPerfData.publishData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
-        }
     }
 
     private synchronized void updateStarted(boolean started) {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
new file mode 100644
index 0000000..01eebf2
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.util.Log;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Parses PSS info from dumpsys meminfo */
+public class MemInfoParser implements PerfParser {
+
+    private static final Pattern LINE_PATTERN =
+        Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*");
+    private static final String PSS_BY_PROCESS = "Total PSS by process:";
+    private static final String TAG = "loadtest.MemInfoParser";
+
+    private boolean mPssStarted;
+    private boolean mPssEnded;
+    private final long mStartTimeMillis;
+
+    public MemInfoParser(long startTimeMillis) {
+        mStartTimeMillis = startTimeMillis;
+    }
+
+    @Override
+    @Nullable
+    public String parseLine(String line) {
+        if (mPssEnded) {
+            return null;
+        }
+        if (!mPssStarted) {
+            if (line.contains(PSS_BY_PROCESS)) {
+                mPssStarted = true;
+            }
+            return null;
+        }
+        if (line.isEmpty()) {
+            mPssEnded = true;
+            return null;
+        }
+        Matcher lineMatcher = LINE_PATTERN.matcher(line);
+        if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
+            if (lineMatcher.group(2).equals("statsd")) {
+                long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
+                return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1));
+            }
+        }
+        return null;
+    }
+
+    private String convertToPss(String input) {
+        return input.replace(",", "");
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
new file mode 100644
index 0000000..d82a0ea
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+
+public class MemoryDataRecorder extends PerfDataRecorder {
+    private static final String TAG = "loadtest.MemoryDataDataRecorder";
+    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+
+    private long mStartTimeMillis;
+    private StringBuilder mSb;
+
+    public MemoryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    @Override
+    public void startRecording(Context context) {
+        mStartTimeMillis = SystemClock.elapsedRealtime();
+        mSb = new StringBuilder();
+    }
+
+    @Override
+    public void onAlarm(Context context) {
+      Log.d(TAG, "GOT ALARM IN MEM");
+        runDumpsysStats(context, DUMP_FILENAME, "meminfo");
+        readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
+    }
+
+    @Override
+    public void stopRecording(Context context) {
+        writeData(context, "meminfo_", "time,pss", mSb);
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
index 57f85b5..81a84f5 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
@@ -15,29 +15,14 @@
  */
 package com.android.statsd.loadtest;
 
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.Log;
-import android.util.StatsLog;
-import android.util.StatsManager;
-import android.view.View;
-import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
-import android.os.IStatsManager;
-import android.os.ServiceManager;
-import android.view.View.OnFocusChangeListener;
 
 public abstract class NumericalWatcher implements TextWatcher {
 
-  private static final String TAG = "NumericalWatcher";
+  private static final String TAG = "loadtest.NumericalWatcher";
 
     private final TextView mTextView;
     private final int mMin;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
index e3e23f5..4665247 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
@@ -16,147 +16,86 @@
 package com.android.statsd.loadtest;
 
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Bundle;
-import android.os.Environment;
+import android.os.SystemClock;
 import android.util.Log;
-import android.os.Debug;
 
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 /** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
-public class PerfData {
+public class PerfData extends PerfDataRecorder {
 
-    private static final String TAG = "PerfData";
-    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+    private static final String TAG = "loadtest.PerfData";
 
-    public void resetData(Context context) {
-        runDumpsysStats(context, "batterystats", "--reset");
+    /** Polling period for performance snapshots like memory. */
+    private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000;
+
+    public final static class PerfAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Intent activityIntent = new Intent(context, LoadtestActivity.class);
+            activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
+            context.startActivity(activityIntent);
+         }
     }
 
-    public void publishData(Context context, boolean placebo, int replication, long bucketMins,
-        long periodSecs, int burst) {
-        publishBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
+    private AlarmManager mAlarmMgr;
+
+    /** Used to periodically poll some dumpsys data. */
+    private PendingIntent mPendingIntent;
+
+    private final Set<PerfDataRecorder> mRecorders;
+
+    public PerfData(Context context, boolean placebo, int replication, long bucketMins,
+        long periodSecs,  int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+        mRecorders = new HashSet();
+        mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
+        mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
+        mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
     }
 
-    private void publishBatteryData(Context context, boolean placebo, int replication,
-        long bucketMins, long periodSecs, int burst) {
-        // Don't use --checkin.
-        runDumpsysStats(context, "batterystats");
-        writeBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
-    }
-
-    private void runDumpsysStats(Context context, String cmd, String... args) {
-        boolean success = false;
-        // Call dumpsys Dump statistics to a file.
-        FileOutputStream fo = null;
-        try {
-            fo = context.openFileOutput(DUMP_FILENAME, Context.MODE_PRIVATE);
-            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
-                Log.w(TAG, "Dumpsys failed.");
-            }
-            success = true;
-        } catch (IOException | SecurityException | NullPointerException e) {
-            // SecurityException may occur when trying to dump multi-user info.
-            // NPE can occur during dumpService  (root cause unknown).
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(fo);
+    public void onDestroy() {
+        if (mPendingIntent != null) {
+            mAlarmMgr.cancel(mPendingIntent);
+            mPendingIntent = null;
         }
     }
 
-    private String readDumpFile(Context context) {
-        StringBuilder sb = new StringBuilder();
-        FileInputStream fi = null;
-        BufferedReader br = null;
-        try {
-            fi = context.openFileInput(DUMP_FILENAME);
-            br = new BufferedReader(new InputStreamReader(fi));
-            String line = br.readLine();
-            while (line != null) {
-                sb.append(line);
-                sb.append(System.lineSeparator());
-                line = br.readLine();
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(br);
-        }
-        return sb.toString();
-    }
+    @Override
+    public void startRecording(Context context) {
+        Intent intent = new Intent(context, PerfAlarmReceiver.class);
+        intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
+        mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
+        mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */,
+            POLLING_PERIOD_MILLIS, mPendingIntent);
 
-    private static void closeQuietly(@Nullable Closeable c) {
-        if (c != null) {
-            try {
-                c.close();
-            } catch (IOException ignore) {
-            }
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.startRecording(context);
         }
     }
 
-    public void writeBatteryData(Context context, boolean placebo, int replication, long bucketMins,
-        long periodSecs, int burst) {
-        BatteryStatsParser parser = new BatteryStatsParser();
-        FileInputStream fi = null;
-        BufferedReader br = null;
-        String suffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
-        File batteryDataFile = new File(getStorageDir(), "battery_" + suffix + ".csv");
-        Log.d(TAG, "Writing battery data to " + batteryDataFile.getAbsolutePath());
-
-        FileWriter writer = null;
-        try {
-            fi = context.openFileInput(DUMP_FILENAME);
-            writer = new FileWriter(batteryDataFile);
-            writer.append("time,battery_level"
-                + getColumnName(placebo, replication, bucketMins, periodSecs, burst) + "\n");
-            br = new BufferedReader(new InputStreamReader(fi));
-            String line = br.readLine();
-            while (line != null) {
-                String recordLine = parser.parseLine(line);
-                if (recordLine != null) {
-                    writer.append(recordLine);
-                }
-                line = br.readLine();
-            }
-            writer.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(writer);
-            closeQuietly(br);
+    @Override
+    public void onAlarm(Context context) {
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.onAlarm(context);
         }
     }
 
-    private File getStorageDir() {
-        File file = new File(Environment.getExternalStoragePublicDirectory(
-            Environment.DIRECTORY_DOCUMENTS), "loadtest");
-        if (!file.mkdirs()) {
-            Log.e(TAG, "Directory not created");
+    @Override
+    public void stopRecording(Context context) {
+        if (mPendingIntent != null) {
+            mAlarmMgr.cancel(mPendingIntent);
+            mPendingIntent = null;
         }
-        return file;
-    }
 
-    private String getColumnName(boolean placebo, int replication, long bucketMins, long periodSecs,
-        int burst) {
-        if (placebo) {
-            return "_placebo_p=" + periodSecs;
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.stopRecording(context);
         }
-        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
     }
 }
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
new file mode 100644
index 0000000..15a8e5c
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Environment;
+import android.util.Log;
+import android.os.Debug;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public abstract class PerfDataRecorder {
+    private static final String TAG = "loadtest.PerfDataRecorder";
+
+    protected final String mFileSuffix;
+    protected final String mColumnSuffix;
+
+    protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        mFileSuffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
+        mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    /** Starts recording performance data. */
+    public abstract void startRecording(Context context);
+
+    /** Called periodically. For the recorder to sample data, if needed. */
+    public abstract void onAlarm(Context context);
+
+    /** Stops recording performance data, and writes it to disk. */
+    public abstract void stopRecording(Context context);
+
+    /** Runs the dumpsys command. */
+    protected void runDumpsysStats(Context context, String dumpFilename, String cmd,
+        String... args) {
+        boolean success = false;
+        // Call dumpsys Dump statistics to a file.
+        FileOutputStream fo = null;
+        try {
+            fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE);
+            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
+                Log.w(TAG, "Dumpsys failed.");
+            }
+            success = true;
+        } catch (IOException | SecurityException | NullPointerException e) {
+            // SecurityException may occur when trying to dump multi-user info.
+            // NPE can occur during dumpService  (root cause unknown).
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(fo);
+        }
+    }
+
+    /**
+     * Reads a text file and parses each line, one by one. The result of the parsing is stored
+     * in the passed {@link StringBuffer}.
+     */
+    protected void readDumpData(Context context, String dumpFilename, PerfParser parser,
+        StringBuilder sb) {
+        FileInputStream fi = null;
+        BufferedReader br = null;
+        try {
+            fi = context.openFileInput(dumpFilename);
+            br = new BufferedReader(new InputStreamReader(fi));
+            String line = br.readLine();
+            while (line != null) {
+                String recordLine = parser.parseLine(line);
+                if (recordLine != null) {
+                  sb.append(recordLine).append('\n');
+                }
+                line = br.readLine();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(br);
+        }
+    }
+
+    /** Writes CSV data to a file. */
+    protected void writeData(Context context, String filePrefix, String columnPrefix,
+        StringBuilder sb) {
+        File dataFile = new File(getStorageDir(), filePrefix + mFileSuffix + ".csv");
+
+        FileWriter writer = null;
+        try {
+            writer = new FileWriter(dataFile);
+            writer.append(columnPrefix + mColumnSuffix + "\n");
+            writer.append(sb.toString());
+            writer.flush();
+            Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(writer);
+        }
+    }
+
+    /** Gets the suffix to use in the column name for perf data. */
+    private String getColumnSuffix(boolean placebo, int replication, long bucketMins,
+        long periodSecs, int burst) {
+        if (placebo) {
+            return "_placebo_p=" + periodSecs;
+        }
+        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
+    }
+
+
+    private File getStorageDir() {
+        File file = new File(Environment.getExternalStoragePublicDirectory(
+            Environment.DIRECTORY_DOCUMENTS), "loadtest");
+        if (!file.mkdirs()) {
+            Log.e(TAG, "Directory not created");
+        }
+        return file;
+    }
+
+    private void closeQuietly(@Nullable Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
new file mode 100644
index 0000000..e000918
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.statsd.loadtest;
+
+import android.annotation.Nullable;
+
+public interface PerfParser {
+
+  /**
+   * Parses one line of the dumpsys output, and returns a string to write to the data file,
+   * or null if no string should be written.
+   */
+  @Nullable String parseLine(String line);
+}
diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index e6cbdb4..008bb93 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
     $(call all-java-files-under, ../library/core-src)
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 LOCAL_MODULE := uiautomator-instrumentation
 # TODO: change this to 18 when it's available
 LOCAL_SDK_VERSION := current
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 4bf856f..22cffe6 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -28,8 +28,8 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(uiautomator.core_src_files)
 LOCAL_MODULE := uiautomator.core
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ###############################################
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 03a3631..aa099eb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7070,7 +7070,13 @@
         mActivityTransitionState.enterReady(this);
     }
 
-    final void performRestart() {
+    /**
+     * Restart the activity.
+     * @param start Indicates whether the activity should also be started after restart.
+     *              The option to not start immediately is needed in case a transaction with
+     *              multiple lifecycle transitions is in progress.
+     */
+    final void performRestart(boolean start) {
         mCanEnterPictureInPicture = true;
         mFragments.noteStateNotSaved();
 
@@ -7108,12 +7114,14 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onRestart()");
             }
-            performStart();
+            if (start) {
+                performStart();
+            }
         }
     }
 
     final void performResume() {
-        performRestart();
+        performRestart(true /* start */);
 
         mFragments.execPendingActions();
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b84833b..20213199 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,13 @@
 
 package android.app;
 
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
@@ -23,8 +30,12 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.PendingTransactionActions;
+import android.app.servertransaction.PendingTransactionActions.StopInfo;
+import android.app.servertransaction.TransactionExecutor;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -84,7 +95,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.provider.BlockedNumberContract;
 import android.provider.CalendarContract;
@@ -102,7 +112,6 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
-import android.util.LogWriter;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -122,6 +131,7 @@
 import android.webkit.WebView;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.BinderInternal;
@@ -129,7 +139,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.proto.MemInfoProto;
@@ -190,7 +199,7 @@
     private static final boolean DEBUG_BACKUP = false;
     public static final boolean DEBUG_CONFIGURATION = false;
     private static final boolean DEBUG_SERVICE = false;
-    private static final boolean DEBUG_MEMORY_TRIM = false;
+    public static final boolean DEBUG_MEMORY_TRIM = false;
     private static final boolean DEBUG_PROVIDER = false;
     private static final boolean DEBUG_ORDER = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
@@ -206,10 +215,6 @@
     /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
     public static final int SERVICE_DONE_EXECUTING_STOP = 2;
 
-    // Details for pausing activity.
-    private static final int USER_LEAVING = 1;
-    private static final int DONT_REPORT = 2;
-
     // Whether to invoke an activity callback after delivering new configuration.
     private static final boolean REPORT_TO_ACTIVITY = true;
 
@@ -289,12 +294,8 @@
     final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
     @GuardedBy("mResourcesManager")
     Configuration mPendingConfiguration = null;
-    // Because we merge activity relaunch operations we can't depend on the ordering provided by
-    // the handler messages. We need to introduce secondary ordering mechanism, which will allow
-    // us to drop certain events, if we know that they happened before relaunch we already executed.
-    // This represents the order of receiving the request from AM.
-    @GuardedBy("mResourcesManager")
-    int mLifecycleSeq = 0;
+    // An executor that performs multi-step transactions.
+    private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
 
     private final ResourcesManager mResourcesManager;
 
@@ -342,8 +343,9 @@
 
     Bundle mCoreSettings = null;
 
-    static final class ActivityClientRecord {
-        IBinder token;
+    /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
+    public static final class ActivityClientRecord {
+        public IBinder token;
         int ident;
         Intent intent;
         String referrer;
@@ -355,6 +357,7 @@
         Activity parent;
         String embeddedID;
         Activity.NonConfigurationInstances lastNonConfigurationInstances;
+        // TODO(lifecycler): Use mLifecycleState instead.
         boolean paused;
         boolean stopped;
         boolean hideForNow;
@@ -371,13 +374,13 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        LoadedApk packageInfo;
+        public LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
 
         boolean startsNotResumed;
-        boolean isForward;
+        public final boolean isForward;
         int pendingConfigChanges;
         boolean onlyLocalRequest;
 
@@ -385,15 +388,42 @@
         WindowManager mPendingRemoveWindowManager;
         boolean mPreserveWindow;
 
-        // Set for relaunch requests, indicates the order number of the relaunch operation, so it
-        // can be compared with other lifecycle operations.
-        int relaunchSeq = 0;
+        @LifecycleState
+        private int mLifecycleState = PRE_ON_CREATE;
 
-        // Can only be accessed from the UI thread. This represents the latest processed message
-        // that is related to lifecycle events/
-        int lastProcessedSeq = 0;
+        @VisibleForTesting
+        public ActivityClientRecord() {
+            this.isForward = false;
+            init();
+        }
 
-        ActivityClientRecord() {
+        public ActivityClientRecord(IBinder token, Intent intent, int ident,
+                ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
+                String referrer, IVoiceInteractor voiceInteractor, Bundle state,
+                PersistableBundle persistentState, List<ResultInfo> pendingResults,
+                List<ReferrerIntent> pendingNewIntents, boolean isForward,
+                ProfilerInfo profilerInfo, ClientTransactionHandler client) {
+            this.token = token;
+            this.ident = ident;
+            this.intent = intent;
+            this.referrer = referrer;
+            this.voiceInteractor = voiceInteractor;
+            this.activityInfo = info;
+            this.compatInfo = compatInfo;
+            this.state = state;
+            this.persistentState = persistentState;
+            this.pendingResults = pendingResults;
+            this.pendingIntents = pendingNewIntents;
+            this.isForward = isForward;
+            this.profilerInfo = profilerInfo;
+            this.overrideConfig = overrideConfig;
+            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
+                    compatInfo);
+            init();
+        }
+
+        /** Common initializer for all constructors. */
+        private void init() {
             parent = null;
             embeddedID = null;
             paused = false;
@@ -410,6 +440,38 @@
             };
         }
 
+        /** Get the current lifecycle state. */
+        public int getLifecycleState() {
+            return mLifecycleState;
+        }
+
+        /** Update the current lifecycle state for internal bookkeeping. */
+        public void setState(@LifecycleState int newLifecycleState) {
+            mLifecycleState = newLifecycleState;
+            switch (mLifecycleState) {
+                case ON_CREATE:
+                    paused = true;
+                    stopped = true;
+                    break;
+                case ON_START:
+                    paused = true;
+                    stopped = false;
+                    break;
+                case ON_RESUME:
+                    paused = false;
+                    stopped = false;
+                    break;
+                case ON_PAUSE:
+                    paused = true;
+                    stopped = false;
+                    break;
+                case ON_STOP:
+                    paused = true;
+                    stopped = true;
+                    break;
+            }
+        }
+
         public boolean isPreHoneycomb() {
             if (activity != null) {
                 return activity.getApplicationInfo().targetSdkVersion
@@ -1315,13 +1377,6 @@
         mAppThread.updateProcessState(processState, fromIpc);
     }
 
-    @Override
-    public int getLifecycleSeq() {
-        synchronized (mResourcesManager) {
-            return mLifecycleSeq++;
-        }
-    }
-
     class H extends Handler {
         public static final int BIND_APPLICATION        = 110;
         public static final int EXIT_APPLICATION        = 111;
@@ -1586,7 +1641,7 @@
                             (String[]) ((SomeArgs) msg.obj).arg2);
                     break;
                 case EXECUTE_TRANSACTION:
-                    ((ClientTransaction) msg.obj).execute(ActivityThread.this);
+                    mTransactionExecutor.execute(((ClientTransaction) msg.obj));
                     break;
             }
             Object obj = msg.obj;
@@ -1796,6 +1851,7 @@
                 registerPackage);
     }
 
+    @Override
     public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
         return getPackageInfo(ai, compatInfo, null, false, true, false);
@@ -2479,13 +2535,21 @@
                     + ", comp=" + name
                     + ", token=" + token);
         }
-        return performLaunchActivity(r, null);
+        // TODO(lifecycler): Can't switch to use #handleLaunchActivity() because it will try to
+        // call #reportSizeConfigurations(), but the server might not know anything about the
+        // activity if it was launched from LocalAcvitivyManager.
+        return performLaunchActivity(r);
     }
 
     public final Activity getActivity(IBinder token) {
         return mActivities.get(token).activity;
     }
 
+    @Override
+    public ActivityClientRecord getActivityClient(IBinder token) {
+        return mActivities.get(token);
+    }
+
     public final void sendActivityResult(
             IBinder token, String id, int requestCode,
             int resultCode, Intent data) {
@@ -2553,9 +2617,8 @@
         sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
-        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
-
+    /**  Core implementation of activity launch. */
+    private Activity performLaunchActivity(ActivityClientRecord r) {
         ActivityInfo aInfo = r.activityInfo;
         if (r.packageInfo == null) {
             r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
@@ -2625,9 +2688,6 @@
                         r.embeddedID, r.lastNonConfigurationInstances, config,
                         r.referrer, r.voiceInteractor, window, r.configCallback);
 
-                if (customIntent != null) {
-                    activity.mIntent = customIntent;
-                }
                 r.lastNonConfigurationInstances = null;
                 checkAndBlockForNetworkAccess();
                 activity.mStartedActivity = false;
@@ -2648,37 +2708,8 @@
                         " did not call through to super.onCreate()");
                 }
                 r.activity = activity;
-                r.stopped = true;
-                if (!r.activity.mFinished) {
-                    activity.performStart();
-                    r.stopped = false;
-                }
-                if (!r.activity.mFinished) {
-                    if (r.isPersistable()) {
-                        if (r.state != null || r.persistentState != null) {
-                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
-                                    r.persistentState);
-                        }
-                    } else if (r.state != null) {
-                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
-                    }
-                }
-                if (!r.activity.mFinished) {
-                    activity.mCalled = false;
-                    if (r.isPersistable()) {
-                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
-                                r.persistentState);
-                    } else {
-                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
-                    }
-                    if (!activity.mCalled) {
-                        throw new SuperNotCalledException(
-                            "Activity " + r.intent.getComponent().toShortString() +
-                            " did not call through to super.onPostCreate()");
-                    }
-                }
             }
-            r.paused = true;
+            r.setState(ON_CREATE);
 
             mActivities.put(r.token, r);
 
@@ -2696,6 +2727,60 @@
         return activity;
     }
 
+    @Override
+    public void handleStartActivity(ActivityClientRecord r,
+            PendingTransactionActions pendingActions) {
+        final Activity activity = r.activity;
+        if (r.activity == null) {
+            // TODO(lifecycler): What do we do in this case?
+            return;
+        }
+        if (!r.stopped) {
+            throw new IllegalStateException("Can't start activity that is not stopped.");
+        }
+        if (r.activity.mFinished) {
+            // TODO(lifecycler): How can this happen?
+            return;
+        }
+
+        // Start
+        activity.performStart();
+        r.setState(ON_START);
+
+        if (pendingActions == null) {
+            // No more work to do.
+            return;
+        }
+
+        // Restore instance state
+        if (pendingActions.shouldRestoreInstanceState()) {
+            if (r.isPersistable()) {
+                if (r.state != null || r.persistentState != null) {
+                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
+                            r.persistentState);
+                }
+            } else if (r.state != null) {
+                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
+            }
+        }
+
+        // Call postOnCreate()
+        if (pendingActions.shouldCallOnPostCreate()) {
+            activity.mCalled = false;
+            if (r.isPersistable()) {
+                mInstrumentation.callActivityOnPostCreate(activity, r.state,
+                        r.persistentState);
+            } else {
+                mInstrumentation.callActivityOnPostCreate(activity, r.state);
+            }
+            if (!activity.mCalled) {
+                throw new SuperNotCalledException(
+                        "Activity " + r.intent.getComponent().toShortString()
+                                + " did not call through to super.onPostCreate()");
+            }
+        }
+    }
+
     /**
      * Checks if {@link #mNetworkBlockSeq} is {@link #INVALID_PROC_STATE_SEQ} and if so, returns
      * immediately. Otherwise, makes a blocking call to ActivityManagerService to wait for the
@@ -2742,38 +2827,12 @@
         return appContext;
     }
 
+    /**
+     * Extended implementation of activity launch. Used when server requests a launch or relaunch.
+     */
     @Override
-    public void handleLaunchActivity(IBinder token, Intent intent, int ident, ActivityInfo info,
-            Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer,
-            IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState,
-            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
-        ActivityClientRecord r = new ActivityClientRecord();
-
-        r.token = token;
-        r.ident = ident;
-        r.intent = intent;
-        r.referrer = referrer;
-        r.voiceInteractor = voiceInteractor;
-        r.activityInfo = info;
-        r.compatInfo = compatInfo;
-        r.state = state;
-        r.persistentState = persistentState;
-
-        r.pendingResults = pendingResults;
-        r.pendingIntents = pendingNewIntents;
-
-        r.startsNotResumed = notResumed;
-        r.isForward = isForward;
-
-        r.profilerInfo = profilerInfo;
-
-        r.overrideConfig = overrideConfig;
-        r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
-        handleLaunchActivity(r, null /* customIntent */, "LAUNCH_ACTIVITY");
-    }
-
-    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
+    public Activity handleLaunchActivity(ActivityClientRecord r,
+            PendingTransactionActions pendingActions) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -2796,44 +2855,28 @@
         }
         WindowManagerGlobal.initialize();
 
-        Activity a = performLaunchActivity(r, customIntent);
+        final Activity a = performLaunchActivity(r);
 
         if (a != null) {
             r.createdConfig = new Configuration(mConfiguration);
             reportSizeConfigurations(r);
-            Bundle oldState = r.state;
-            handleResumeActivity(r.token, false, r.isForward,
-                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
-
-            if (!r.activity.mFinished && r.startsNotResumed) {
-                // The activity manager actually wants this one to start out paused, because it
-                // needs to be visible but isn't in the foreground. We accomplish this by going
-                // through the normal startup (because activities expect to go through onResume()
-                // the first time they run, before their window is displayed), and then pausing it.
-                // However, in this case we do -not- need to do the full pause cycle (of freezing
-                // and such) because the activity manager assumes it can just retain the current
-                // state it has.
-                performPauseActivityIfNeeded(r, reason);
-
-                // We need to keep around the original state, in case we need to be created again.
-                // But we only do this for pre-Honeycomb apps, which always save their state when
-                // pausing, so we can not have them save their state when restarting from a paused
-                // state. For HC and later, we want to (and can) let the state be saved as the
-                // normal part of stopping the activity.
-                if (r.isPreHoneycomb()) {
-                    r.state = oldState;
-                }
+            if (!r.activity.mFinished && pendingActions != null) {
+                pendingActions.setOldState(r.state);
+                pendingActions.setRestoreInstanceState(true);
+                pendingActions.setCallOnPostCreate(true);
             }
         } else {
             // If there was an error, for any reason, tell the activity manager to stop us.
             try {
                 ActivityManager.getService()
-                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
-                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
+                        .finishActivity(r.token, Activity.RESULT_CANCELED, null,
+                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
         }
+
+        return a;
     }
 
     private void reportSizeConfigurations(ActivityClientRecord r) {
@@ -3477,8 +3520,7 @@
         //Slog.i(TAG, "Running services: " + mServices);
     }
 
-    public final ActivityClientRecord performResumeActivity(IBinder token,
-            boolean clearHide, String reason) {
+    ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                 + " finished=" + r.activity.mFinished);
@@ -3518,10 +3560,9 @@
                 EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), reason);
 
-                r.paused = false;
-                r.stopped = false;
                 r.state = null;
                 r.persistentState = null;
+                r.setState(ON_RESUME);
             } catch (Exception e) {
                 if (!mInstrumentation.onException(r.activity, e)) {
                     throw new RuntimeException(
@@ -3553,19 +3594,14 @@
 
     @Override
     public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            boolean reallyResume, int seq, String reason) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
-            return;
-        }
-
+            String reason) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
         mSomeActivitiesChanged = true;
 
         // TODO Push resumeArgs into the activity for consideration
-        r = performResumeActivity(token, clearHide, reason);
+        final ActivityClientRecord r = performResumeActivity(token, clearHide, reason);
 
         if (r != null) {
             final Activity a = r.activity;
@@ -3677,16 +3713,6 @@
                 Looper.myQueue().addIdleHandler(new Idler());
             }
             r.onlyLocalRequest = false;
-
-            // Tell the activity manager we have resumed.
-            if (reallyResume) {
-                try {
-                    ActivityManager.getService().activityResumed(token);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-            }
-
         } else {
             // If an exception was thrown when trying to resume, then
             // just end this activity.
@@ -3757,21 +3783,17 @@
     }
 
     @Override
-    public void handlePauseActivity(IBinder token, boolean finished,
-            boolean userLeaving, int configChanges, boolean dontReport, int seq) {
+    public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+            int configChanges, boolean dontReport, PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
-            return;
-        }
         if (r != null) {
-            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
             if (userLeaving) {
                 performUserLeavingActivity(r);
             }
 
             r.activity.mConfigChangeFlags |= configChanges;
-            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
+            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity",
+                    pendingActions);
 
             // Make sure any pending writes are now committed.
             if (r.isPreHoneycomb()) {
@@ -3795,13 +3817,15 @@
     }
 
     final Bundle performPauseActivity(IBinder token, boolean finished,
-            boolean saveState, String reason) {
+            boolean saveState, String reason, PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        return r != null ? performPauseActivity(r, finished, saveState, reason) : null;
+        return r != null
+                ? performPauseActivity(r, finished, saveState, reason, pendingActions)
+                : null;
     }
 
-    final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
-            boolean saveState, String reason) {
+    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, boolean saveState,
+            String reason, PendingTransactionActions pendingActions) {
         if (r.paused) {
             if (r.activity.mFinished) {
                 // If we are finishing, we won't call onResume() in certain cases.
@@ -3835,6 +3859,18 @@
             listeners.get(i).onPaused(r.activity);
         }
 
+        final Bundle oldState = pendingActions != null ? pendingActions.getOldState() : null;
+        if (oldState != null) {
+            // We need to keep around the original state, in case we need to be created again.
+            // But we only do this for pre-Honeycomb apps, which always save their state when
+            // pausing, so we can not have them save their state when restarting from a paused
+            // state. For HC and later, we want to (and can) let the state be saved as the
+            // normal part of stopping the activity.
+            if (r.isPreHoneycomb()) {
+                r.state = oldState;
+            }
+        }
+
         return !r.activity.mFinished && saveState ? r.state : null;
     }
 
@@ -3861,7 +3897,7 @@
                         + safeToComponentShortString(r.intent) + ": " + e.toString(), e);
             }
         }
-        r.paused = true;
+        r.setState(ON_PAUSE);
     }
 
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
@@ -3869,37 +3905,6 @@
         performStopActivityInner(r, null, false, saveState, reason);
     }
 
-    private static class StopInfo implements Runnable {
-        ActivityClientRecord activity;
-        Bundle state;
-        PersistableBundle persistentState;
-        CharSequence description;
-
-        @Override public void run() {
-            // Tell activity manager we have been stopped.
-            try {
-                if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
-                ActivityManager.getService().activityStopped(
-                    activity.token, state, persistentState, description);
-            } catch (RemoteException ex) {
-                // Dump statistics about bundle to help developers debug
-                final LogWriter writer = new LogWriter(Log.WARN, TAG);
-                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-                pw.println("Bundle stats:");
-                Bundle.dumpStats(pw, state);
-                pw.println("PersistableBundle stats:");
-                Bundle.dumpStats(pw, persistentState);
-
-                if (ex instanceof TransactionTooLargeException
-                        && activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
-                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
-                    return;
-                }
-                throw ex.rethrowFromSystemServer();
-            }
-        }
-    }
-
     private static final class ProviderRefCount {
         public final ContentProviderHolder holder;
         public final ProviderClientRecord client;
@@ -3930,8 +3935,8 @@
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibility changes.
      */
-    private void performStopActivityInner(ActivityClientRecord r,
-            StopInfo info, boolean keepShown, boolean saveState, String reason) {
+    private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
+            boolean saveState, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
             if (!keepShown && r.stopped) {
@@ -3956,7 +3961,7 @@
                     // First create a thumbnail for the activity...
                     // For now, don't create the thumbnail here; we are
                     // doing that by doing a screen snapshot.
-                    info.description = r.activity.onCreateDescription();
+                    info.setDescription(r.activity.onCreateDescription());
                 } catch (Exception e) {
                     if (!mInstrumentation.onException(r.activity, e)) {
                         throw new RuntimeException(
@@ -3986,7 +3991,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), reason);
             }
@@ -4022,15 +4027,13 @@
     }
 
     @Override
-    public void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
-            return;
-        }
+    public void handleStopActivity(IBinder token, boolean show, int configChanges,
+            PendingTransactionActions pendingActions) {
+        final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
-        StopInfo info = new StopInfo();
-        performStopActivityInner(r, info, show, true, "handleStopActivity");
+        final StopInfo stopInfo = new StopInfo();
+        performStopActivityInner(r, stopInfo, show, true, "handleStopActivity");
 
         if (localLOGV) Slog.v(
             TAG, "Finishing stop of " + r + ": show=" + show
@@ -4043,37 +4046,32 @@
             QueuedWork.waitToFinish();
         }
 
-        // Schedule the call to tell the activity manager we have
-        // stopped.  We don't do this immediately, because we want to
-        // have a chance for any other pending work (in particular memory
-        // trim requests) to complete before you tell the activity
-        // manager to proceed and allow us to go fully into the background.
-        info.activity = r;
-        info.state = r.state;
-        info.persistentState = r.persistentState;
-        mH.post(info);
+        stopInfo.setActivity(r);
+        stopInfo.setState(r.state);
+        stopInfo.setPersistentState(r.persistentState);
+        pendingActions.setStopInfo(stopInfo);
         mSomeActivitiesChanged = true;
     }
 
-    private static boolean checkAndUpdateLifecycleSeq(int seq, ActivityClientRecord r,
-            String action) {
-        if (r == null) {
-            return true;
-        }
-        if (seq < r.lastProcessedSeq) {
-            if (DEBUG_ORDER) Slog.d(TAG, action + " for " + r + " ignored, because seq=" + seq
-                    + " < mCurrentLifecycleSeq=" + r.lastProcessedSeq);
-            return false;
-        }
-        r.lastProcessedSeq = seq;
-        return true;
+    /**
+     * Schedule the call to tell the activity manager we have stopped.  We don't do this
+     * immediately, because we want to have a chance for any other pending work (in particular
+     * memory trim requests) to complete before you tell the activity manager to proceed and allow
+     * us to go fully into the background.
+     */
+    @Override
+    public void reportStop(PendingTransactionActions pendingActions) {
+        mH.post(pendingActions.getStopInfo());
     }
 
-    final void performRestartActivity(IBinder token) {
+    @Override
+    public void performRestartActivity(IBinder token, boolean start) {
         ActivityClientRecord r = mActivities.get(token);
         if (r.stopped) {
-            r.activity.performRestart();
-            r.stopped = false;
+            r.activity.performRestart(start);
+            if (start) {
+                r.setState(ON_START);
+            }
         }
     }
 
@@ -4093,8 +4091,8 @@
             // we are back active so skip it.
             unscheduleGcIdler();
 
-            r.activity.performRestart();
-            r.stopped = false;
+            r.activity.performRestart(true /* start */);
+            r.setState(ON_START);
         }
         if (r.activity.mDecor != null) {
             if (false) Slog.v(
@@ -4132,7 +4130,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), "sleeping");
             }
@@ -4150,8 +4148,8 @@
             }
         } else {
             if (r.stopped && r.activity.mVisibleFromServer) {
-                r.activity.performRestart();
-                r.stopped = false;
+                r.activity.performRestart(true /* start */);
+                r.setState(ON_START);
             }
         }
     }
@@ -4268,11 +4266,8 @@
         }
     }
 
-    public final ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing) {
-        return performDestroyActivity(token, finishing, 0, false);
-    }
-
-    private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
+    /** Core implementation of activity destroy call. */
+    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance) {
         ActivityClientRecord r = mActivities.get(token);
         Class<? extends Activity> activityClass = null;
@@ -4299,7 +4294,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), "destroy");
             }
@@ -4336,6 +4331,7 @@
                             + ": " + e.toString(), e);
                 }
             }
+            r.setState(ON_DESTROY);
         }
         mActivities.remove(token);
         StrictMode.decrementExpectedActivityCount(activityClass);
@@ -4496,10 +4492,7 @@
                 target.overrideConfig = overrideConfig;
             }
             target.pendingConfigChanges |= configChanges;
-            target.relaunchSeq = getLifecycleSeq();
         }
-        if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + ", target "
-                + target + " operation received seq: " + target.relaunchSeq);
     }
 
     private void handleRelaunchActivity(ActivityClientRecord tmp) {
@@ -4544,12 +4537,6 @@
             }
         }
 
-        if (tmp.lastProcessedSeq > tmp.relaunchSeq) {
-            Slog.wtf(TAG, "For some reason target: " + tmp + " has lower sequence: "
-                    + tmp.relaunchSeq + " than current sequence: " + tmp.lastProcessedSeq);
-        } else {
-            tmp.lastProcessedSeq = tmp.relaunchSeq;
-        }
         if (tmp.createdConfig != null) {
             // If the activity manager is passing us its current config,
             // assume that is really what we want regardless of what we
@@ -4590,9 +4577,6 @@
         r.activity.mConfigChangeFlags |= configChanges;
         r.onlyLocalRequest = tmp.onlyLocalRequest;
         r.mPreserveWindow = tmp.mPreserveWindow;
-        r.lastProcessedSeq = tmp.lastProcessedSeq;
-        r.relaunchSeq = tmp.relaunchSeq;
-        Intent currentIntent = r.activity.mIntent;
 
         r.activity.mChangingConfigurations = true;
 
@@ -4618,7 +4602,8 @@
 
         // Need to ensure state is saved.
         if (!r.paused) {
-            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity");
+            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity",
+                    null /* pendingActions */);
         }
         if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
             callCallActivityOnSaveInstanceState(r);
@@ -4648,7 +4633,15 @@
         r.startsNotResumed = tmp.startsNotResumed;
         r.overrideConfig = tmp.overrideConfig;
 
-        handleLaunchActivity(r, currentIntent, "handleRelaunchActivity");
+        // TODO(lifecycler): Move relaunch to lifecycler.
+        PendingTransactionActions pendingActions = new PendingTransactionActions();
+        handleLaunchActivity(r, pendingActions);
+        handleStartActivity(r, pendingActions);
+        handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
+        if (r.startsNotResumed) {
+            performPauseActivity(r, false /* finished */, r.isPreHoneycomb(), "relaunch",
+                    pendingActions);
+        }
 
         if (!tmp.onlyLocalRequest) {
             try {
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f7f4c71..ef66af0 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -16,15 +16,12 @@
 package android.app;
 
 import android.app.servertransaction.ClientTransaction;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
+import android.app.servertransaction.PendingTransactionActions;
+import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.os.IBinder;
-import android.os.PersistableBundle;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 
 import java.util.List;
@@ -40,7 +37,7 @@
 
     /** Prepare and schedule transaction for execution. */
     void scheduleTransaction(ClientTransaction transaction) {
-        transaction.prepare(this);
+        transaction.preExecute(this);
         sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
     }
 
@@ -50,9 +47,6 @@
     // Prepare phase related logic and handlers. Methods that inform about about pending changes or
     // do other internal bookkeeping.
 
-    /** Get current lifecycle request number to maintain correct ordering. */
-    public abstract int getLifecycleSeq();
-
     /** Set pending config in case it will be updated by other transaction item. */
     public abstract void updatePendingConfiguration(Configuration config);
 
@@ -69,15 +63,21 @@
 
     /** Pause the activity. */
     public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, int seq);
+            int configChanges, boolean dontReport, PendingTransactionActions pendingActions);
 
     /** Resume the activity. */
     public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            boolean reallyResume, int seq, String reason);
+            String reason);
 
     /** Stop the activity. */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
-            int seq);
+            PendingTransactionActions pendingActions);
+
+    /** Report that activity was stopped to server. */
+    public abstract void reportStop(PendingTransactionActions pendingActions);
+
+    /** Restart the activity after it was stopped. */
+    public abstract void performRestartActivity(IBinder token, boolean start);
 
     /** Deliver activity (override) configuration change. */
     public abstract void handleActivityConfigurationChanged(IBinder activityToken,
@@ -102,13 +102,23 @@
     public abstract void handleWindowVisibility(IBinder token, boolean show);
 
     /** Perform activity launch. */
-    public abstract void handleLaunchActivity(IBinder token, Intent intent, int ident,
-            ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
-            String referrer, IVoiceInteractor voiceInteractor, Bundle state,
-            PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo);
+    public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r,
+            PendingTransactionActions pendingActions);
+
+    /** Perform activity start. */
+    public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r,
+            PendingTransactionActions pendingActions);
+
+    /** Get package info. */
+    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+            CompatibilityInfo compatInfo);
 
     /** Deliver app configuration change notification. */
     public abstract void handleConfigurationChanged(Configuration config);
+
+    /**
+     * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
+     * provided token.
+     */
+    public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
 }
diff --git a/core/java/android/app/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/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 3b273bc..998ac5f 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.Window;
+
 import com.android.internal.content.ReferrerIntent;
 
 import java.util.ArrayList;
@@ -161,12 +162,12 @@
             case CREATED:
                 if (desiredState == STARTED) {
                     if (localLOGV) Log.v(TAG, r.id + ": restarting");
-                    mActivityThread.performRestartActivity(r);
+                    mActivityThread.performRestartActivity(r, true /* start */);
                     r.curState = STARTED;
                 }
                 if (desiredState == RESUMED) {
                     if (localLOGV) Log.v(TAG, r.id + ": restarting and resuming");
-                    mActivityThread.performRestartActivity(r);
+                    mActivityThread.performRestartActivity(r, true /* start */);
                     mActivityThread.performResumeActivity(r, true, "moveToState-CREATED");
                     r.curState = RESUMED;
                 }
@@ -207,7 +208,7 @@
     private void performPause(LocalActivityRecord r, boolean finishing) {
         final boolean needState = r.instanceState == null;
         final Bundle instanceState = mActivityThread.performPauseActivity(
-                r, finishing, needState, "performPause");
+                r, finishing, needState, "performPause", null /* pendingActions */);
         if (needState) {
             r.instanceState = instanceState;
         }
@@ -361,7 +362,8 @@
             performPause(r, finish);
         }
         if (localLOGV) Log.v(TAG, r.id + ": destroying");
-        mActivityThread.performDestroyActivity(r, finish);
+        mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
+                false /* getNonConfigInstance */);
         r.activity = null;
         r.window = null;
         if (finish) {
@@ -625,7 +627,8 @@
         for (int i=0; i<N; i++) {
             LocalActivityRecord r = mActivityArray.get(i);
             if (localLOGV) Log.v(TAG, r.id + ": destroying");
-            mActivityThread.performDestroyActivity(r, finishing);
+            mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
+                    false /* getNonConfigInstance */);
         }
         mActivities.clear();
         mActivityArray.clear();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index db1f3af..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.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1f17dbc..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);
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 07001e2..a3fe686c 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -37,7 +38,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
         client.handleActivityConfigurationChanged(token, mConfiguration, INVALID_DISPLAY);
@@ -85,4 +87,9 @@
     public int hashCode() {
         return mConfiguration.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ActivityConfigurationChange{config=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index a64108d..24141e5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -27,16 +27,19 @@
  */
 public abstract class ActivityLifecycleItem extends ClientTransactionItem {
 
-    static final boolean DEBUG_ORDER = false;
-
-    @IntDef({UNDEFINED, RESUMED, PAUSED, STOPPED, DESTROYED})
+    @IntDef({UNDEFINED, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP,
+            ON_DESTROY, ON_RESTART})
     @Retention(RetentionPolicy.SOURCE)
-    @interface LifecycleState{}
+    public @interface LifecycleState{}
     public static final int UNDEFINED = -1;
-    public static final int RESUMED = 0;
-    public static final int PAUSED = 1;
-    public static final int STOPPED = 2;
-    public static final int DESTROYED = 3;
+    public static final int PRE_ON_CREATE = 0;
+    public static final int ON_CREATE = 1;
+    public static final int ON_START = 2;
+    public static final int ON_RESUME = 3;
+    public static final int ON_PAUSE = 4;
+    public static final int ON_STOP = 5;
+    public static final int ON_DESTROY = 6;
+    public static final int ON_RESTART = 7;
 
     /** A final lifecycle state that an activity should reach. */
     @LifecycleState
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 76664d8..3a3d5b9 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,9 +16,10 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.app.ResultInfo;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -41,11 +42,12 @@
 
     @Override
     public int getPreExecutionState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
         client.handleSendResult(token, mResultInfoList);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -92,4 +94,9 @@
     public int hashCode() {
         return mResultInfoList.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ActivityResultItem{resultInfoList=" + mResultInfoList + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
index 4bd01af..e3473bf 100644
--- a/core/java/android/app/servertransaction/BaseClientRequest.java
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -33,13 +33,25 @@
      * @param client Target client handler.
      * @param token  Target activity token.
      */
-    default void prepare(ClientTransactionHandler client, IBinder token) {
+    default void preExecute(ClientTransactionHandler client, IBinder token) {
     }
 
     /**
      * Execute the request.
      * @param client Target client handler.
      * @param token Target activity token.
+     * @param pendingActions Container that may have data pending to be used.
      */
-    void execute(ClientTransactionHandler client, IBinder token);
+    void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions);
+
+    /**
+     * Perform all actions that need to happen after execution, e.g. report the result to server.
+     * @param client Target client handler.
+     * @param token Target activity token.
+     * @param pendingActions Container that may have data pending to be used.
+     */
+    default void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+    }
 }
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index d2289ba..7a58962 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
 import android.app.IApplicationThread;
 import android.os.IBinder;
@@ -69,6 +70,23 @@
         mActivityCallbacks.add(activityCallback);
     }
 
+    /** Get the list of callbacks. */
+    @Nullable
+    List<ClientTransactionItem> getCallbacks() {
+        return mActivityCallbacks;
+    }
+
+    /** Get the target activity. */
+    @Nullable
+    public IBinder getActivityToken() {
+        return mActivityToken;
+    }
+
+    /** Get the target state lifecycle request. */
+    ActivityLifecycleItem getLifecycleStateRequest() {
+        return mLifecycleStateRequest;
+    }
+
     /**
      * Set the lifecycle state in which the client should be after executing the transaction.
      * @param stateRequest A lifecycle request initialized with right parameters.
@@ -82,44 +100,27 @@
      * @param clientTransactionHandler Handler on the client side that will executed all operations
      *                                 requested by transaction items.
      */
-    public void prepare(android.app.ClientTransactionHandler clientTransactionHandler) {
+    public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
         if (mActivityCallbacks != null) {
             final int size = mActivityCallbacks.size();
             for (int i = 0; i < size; ++i) {
-                mActivityCallbacks.get(i).prepare(clientTransactionHandler, mActivityToken);
+                mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
             }
         }
         if (mLifecycleStateRequest != null) {
-            mLifecycleStateRequest.prepare(clientTransactionHandler, mActivityToken);
-        }
-    }
-
-    /**
-     * Execute the transaction.
-     * @param clientTransactionHandler Handler on the client side that will execute all operations
-     *                                 requested by transaction items.
-     */
-    public void execute(android.app.ClientTransactionHandler clientTransactionHandler) {
-        if (mActivityCallbacks != null) {
-            final int size = mActivityCallbacks.size();
-            for (int i = 0; i < size; ++i) {
-                mActivityCallbacks.get(i).execute(clientTransactionHandler, mActivityToken);
-            }
-        }
-        if (mLifecycleStateRequest != null) {
-            mLifecycleStateRequest.execute(clientTransactionHandler, mActivityToken);
+            mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
         }
     }
 
     /**
      * Schedule the transaction after it was initialized. It will be send to client and all its
      * individual parts will be applied in the following sequence:
-     * 1. The client calls {@link #prepare(ClientTransactionHandler)}, which triggers all work that
-     *    needs to be done before actually scheduling the transaction for callbacks and lifecycle
-     *    state request.
+     * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work
+     *    that needs to be done before actually scheduling the transaction for callbacks and
+     *    lifecycle state request.
      * 2. The transaction message is scheduled.
-     * 3. The client calls {@link #execute(ClientTransactionHandler)}, which executes all callbacks
-     *    and necessary lifecycle transitions.
+     * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
+     *    all callbacks and necessary lifecycle transitions.
      */
     public void schedule() throws RemoteException {
         mClient.scheduleTransaction(this);
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 055923e..ee1effa 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -33,12 +34,13 @@
     }
 
     @Override
-    public void prepare(android.app.ClientTransactionHandler client, IBinder token) {
+    public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
         client.updatePendingConfiguration(mConfiguration);
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handleConfigurationChanged(mConfiguration);
     }
 
@@ -82,4 +84,9 @@
     public int hashCode() {
         return mConfiguration.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ConfigurationChangeItem{config=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 38fd5fb..3012a7a 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -38,7 +38,8 @@
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
         client.handleDestroyActivity(token, mFinished, mConfigChanges,
                 false /* getNonConfigInstance */);
@@ -47,7 +48,7 @@
 
     @Override
     public int getTargetState() {
-        return DESTROYED;
+        return ON_DESTROY;
     }
 
 
@@ -96,4 +97,10 @@
         result = 31 * result + mConfigChanges;
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DestroyActivityItem{finished=" + mFinished + ",mConfigChanges="
+                + mConfigChanges + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 417ebac..e39042f 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
@@ -42,7 +43,7 @@
  * Request to launch an activity.
  * @hide
  */
-public class LaunchActivityItem extends ActivityLifecycleItem {
+public class LaunchActivityItem extends ClientTransactionItem {
 
     private final Intent mIntent;
     private final int mIdent;
@@ -57,8 +58,6 @@
     private final PersistableBundle mPersistentState;
     private final List<ResultInfo> mPendingResults;
     private final List<ReferrerIntent> mPendingNewIntents;
-    // TODO(lifecycler): use lifecycle request instead of this param.
-    private final boolean mNotResumed;
     private final boolean mIsForward;
     private final ProfilerInfo mProfilerInfo;
 
@@ -66,8 +65,7 @@
             Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
             String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo) {
+            List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) {
         mIntent = intent;
         mIdent = ident;
         mInfo = info;
@@ -81,31 +79,28 @@
         mPersistentState = persistentState;
         mPendingResults = pendingResults;
         mPendingNewIntents = pendingNewIntents;
-        mNotResumed = notResumed;
         mIsForward = isForward;
         mProfilerInfo = profilerInfo;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
         client.updateProcessState(mProcState, false);
         client.updatePendingConfiguration(mCurConfig);
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-        client.handleLaunchActivity(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo,
-                mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults,
-                mPendingNewIntents, mNotResumed, mIsForward, mProfilerInfo);
+        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
+                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+                mPendingResults, mPendingNewIntents, mIsForward,
+                mProfilerInfo, client);
+        client.handleLaunchActivity(r, pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    @Override
-    public int getTargetState() {
-        return mNotResumed ? PAUSED : RESUMED;
-    }
-
 
     // Parcelable implementation
 
@@ -125,7 +120,6 @@
         dest.writePersistableBundle(mPersistentState);
         dest.writeTypedList(mPendingResults, flags);
         dest.writeTypedList(mPendingNewIntents, flags);
-        dest.writeBoolean(mNotResumed);
         dest.writeBoolean(mIsForward);
         dest.writeTypedObject(mProfilerInfo, flags);
     }
@@ -145,7 +139,6 @@
         mPersistentState = in.readPersistableBundle(getClass().getClassLoader());
         mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
         mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
-        mNotResumed = in.readBoolean();
         mIsForward = in.readBoolean();
         mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR);
     }
@@ -179,7 +172,7 @@
                 && areBundlesEqual(mPersistentState, other.mPersistentState)
                 && Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
-                && mNotResumed == other.mNotResumed && mIsForward == other.mIsForward
+                && mIsForward == other.mIsForward
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo);
     }
 
@@ -197,7 +190,6 @@
         result = 31 * result + (mPersistentState != null ? mPersistentState.size() : 0);
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
-        result = 31 * result + (mNotResumed ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + Objects.hashCode(mProfilerInfo);
         return result;
@@ -229,4 +221,14 @@
         }
         return true;
     }
+
+    @Override
+    public String toString() {
+        return "LaunchActivityItem{intent=" + mIntent + ",ident=" + mIdent + ",info=" + mInfo
+                + ",curConfig=" + mCurConfig + ",overrideConfig=" + mOverrideConfig
+                + ",referrer=" + mReferrer + ",procState=" + mProcState + ",state=" + mState
+                + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
+                + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo
+                + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index ccd80d8..ee87261 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -38,7 +39,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
         client.handleActivityConfigurationChanged(token, mConfiguration, mTargetDisplayId);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -90,4 +92,10 @@
         result = 31 * result + mConfiguration.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "MoveToDisplayItem{targetDisplayId=" + mTargetDisplayId
+                + ",configuration=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
index a0c617f..04ddb5e 100644
--- a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
+++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -38,7 +39,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handleMultiWindowModeChanged(token, mIsInMultiWindowMode, mOverrideConfig);
     }
 
@@ -89,4 +91,10 @@
         result = 31 * result + mOverrideConfig.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "MultiWindowModeChangeItem{isInMultiWindowMode=" + mIsInMultiWindowMode
+                + ",overrideConfig=" + mOverrideConfig + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 61a8965..d01b455 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -16,9 +16,7 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
-import static android.app.servertransaction.ActivityLifecycleItem.RESUMED;
-
+import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -42,18 +40,20 @@
         mPause = pause;
     }
 
-    @Override
+    // TODO(lifecycler): Switch new intent handling to this scheme.
+    /*@Override
     public int getPreExecutionState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
     @Override
     public int getPostExecutionState() {
-        return RESUMED;
-    }
+        return ON_RESUME;
+    }*/
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
         client.handleNewIntent(token, mIntents, mPause);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -105,4 +105,9 @@
         result = 31 * result + mIntents.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "NewIntentItem{pause=" + mPause + ",intents=" + mIntents + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index e561a4b..634da04 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -18,11 +18,12 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to paused state.
@@ -37,7 +38,10 @@
     private final int mConfigChanges;
     private final boolean mDontReport;
 
-    private int mLifecycleSeq;
+    public PauseActivityItem() {
+        this(false /* finished */, false /* userLeaving */, 0 /* configChanges */,
+                true /* dontReport */);
+    }
 
     public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges,
             boolean dontReport) {
@@ -48,27 +52,32 @@
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Pause transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
-        }
-    }
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
         client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
-                mLifecycleSeq);
+                pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
     public int getTargetState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
+    @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        if (mDontReport) {
+            return;
+        }
+        try {
+            // TODO(lifecycler): Use interface callback instead of AMS.
+            ActivityManager.getService().activityPaused(token);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
 
     // Parcelable implementation
 
@@ -122,4 +131,10 @@
         result = 31 * result + (mDontReport ? 1 : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "PauseActivityItem{finished=" + mFinished + ",userLeaving=" + mUserLeaving
+                + ",configChanges=" + mConfigChanges + ",dontReport=" + mDontReport + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
new file mode 100644
index 0000000..073d28c
--- /dev/null
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_MEMORY_TRIM;
+
+import android.app.ActivityManager;
+import android.app.ActivityThread.ActivityClientRecord;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
+import android.util.Log;
+import android.util.LogWriter;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * Container that has data pending to be used at later stages of
+ * {@link android.app.servertransaction.ClientTransaction}.
+ * An instance of this class is passed to each individual transaction item, so it can use some
+ * information from previous steps or add some for the following steps.
+ *
+ * @hide
+ */
+public class PendingTransactionActions {
+    private boolean mRestoreInstanceState;
+    private boolean mCallOnPostCreate;
+    private Bundle mOldState;
+    private StopInfo mStopInfo;
+
+    public PendingTransactionActions() {
+        clear();
+    }
+
+    /** Reset the state of the instance to default, non-initialized values. */
+    public void clear() {
+        mRestoreInstanceState = false;
+        mCallOnPostCreate = false;
+        mOldState = null;
+        mStopInfo = null;
+    }
+
+    /** Getter */
+    public boolean shouldRestoreInstanceState() {
+        return mRestoreInstanceState;
+    }
+
+    public void setRestoreInstanceState(boolean restoreInstanceState) {
+        mRestoreInstanceState = restoreInstanceState;
+    }
+
+    /** Getter */
+    public boolean shouldCallOnPostCreate() {
+        return mCallOnPostCreate;
+    }
+
+    public void setCallOnPostCreate(boolean callOnPostCreate) {
+        mCallOnPostCreate = callOnPostCreate;
+    }
+
+    public Bundle getOldState() {
+        return mOldState;
+    }
+
+    public void setOldState(Bundle oldState) {
+        mOldState = oldState;
+    }
+
+    public StopInfo getStopInfo() {
+        return mStopInfo;
+    }
+
+    public void setStopInfo(StopInfo stopInfo) {
+        mStopInfo = stopInfo;
+    }
+
+    /** Reports to server about activity stop. */
+    public static class StopInfo implements Runnable {
+        private static final String TAG = "ActivityStopInfo";
+
+        private ActivityClientRecord mActivity;
+        private Bundle mState;
+        private PersistableBundle mPersistentState;
+        private CharSequence mDescription;
+
+        public void setActivity(ActivityClientRecord activity) {
+            mActivity = activity;
+        }
+
+        public void setState(Bundle state) {
+            mState = state;
+        }
+
+        public void setPersistentState(PersistableBundle persistentState) {
+            mPersistentState = persistentState;
+        }
+
+        public void setDescription(CharSequence description) {
+            mDescription = description;
+        }
+
+        @Override
+        public void run() {
+            // Tell activity manager we have been stopped.
+            try {
+                if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
+                // TODO(lifecycler): Use interface callback instead of AMS.
+                ActivityManager.getService().activityStopped(
+                        mActivity.token, mState, mPersistentState, mDescription);
+            } catch (RemoteException ex) {
+                // Dump statistics about bundle to help developers debug
+                final LogWriter writer = new LogWriter(Log.WARN, TAG);
+                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+                pw.println("Bundle stats:");
+                Bundle.dumpStats(pw, mState);
+                pw.println("PersistableBundle stats:");
+                Bundle.dumpStats(pw, mPersistentState);
+
+                if (ex instanceof TransactionTooLargeException
+                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
+                    return;
+                }
+                throw ex.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java
index 923839e..7c74e6f 100644
--- a/core/java/android/app/servertransaction/PipModeChangeItem.java
+++ b/core/java/android/app/servertransaction/PipModeChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -37,7 +38,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handlePictureInPictureModeChanged(token, mIsInPipMode, mOverrideConfig);
     }
 
@@ -86,4 +88,10 @@
         result = 31 * result + mOverrideConfig.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "PipModeChangeItem{isInPipMode=" + mIsInPipMode
+                + ",overrideConfig=" + mOverrideConfig + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index ea31a46..d659b80 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -18,11 +18,12 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to resumed state.
@@ -33,36 +34,50 @@
     private static final String TAG = "ResumeActivityItem";
 
     private final int mProcState;
+    private final boolean mUpdateProcState;
     private final boolean mIsForward;
 
-    private int mLifecycleSeq;
+    public ResumeActivityItem(boolean isForward) {
+        mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
+        mUpdateProcState = false;
+        mIsForward = isForward;
+    }
 
     public ResumeActivityItem(int procState, boolean isForward) {
         mProcState = procState;
+        mUpdateProcState = true;
         mIsForward = isForward;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Resume transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
+        if (mUpdateProcState) {
+            client.updateProcessState(mProcState, false);
         }
-        client.updateProcessState(mProcState, false);
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-        client.handleResumeActivity(token, true /* clearHide */, mIsForward,
-                true /* reallyResume */, mLifecycleSeq, "RESUME_ACTIVITY");
+        client.handleResumeActivity(token, true /* clearHide */, mIsForward, "RESUME_ACTIVITY");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        try {
+            // TODO(lifecycler): Use interface callback instead of AMS.
+            ActivityManager.getService().activityResumed(token);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public int getTargetState() {
-        return RESUMED;
+        return ON_RESUME;
     }
 
 
@@ -72,12 +87,14 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mProcState);
+        dest.writeBoolean(mUpdateProcState);
         dest.writeBoolean(mIsForward);
     }
 
     /** Read from Parcel. */
     private ResumeActivityItem(Parcel in) {
         mProcState = in.readInt();
+        mUpdateProcState = in.readBoolean();
         mIsForward = in.readBoolean();
     }
 
@@ -101,14 +118,22 @@
             return false;
         }
         final ResumeActivityItem other = (ResumeActivityItem) o;
-        return mProcState == other.mProcState && mIsForward == other.mIsForward;
+        return mProcState == other.mProcState && mUpdateProcState == other.mUpdateProcState
+                && mIsForward == other.mIsForward;
     }
 
     @Override
     public int hashCode() {
         int result = 17;
         result = 31 * result + mProcState;
+        result = 31 * result + (mUpdateProcState ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "ResumeActivityItem{procState=" + mProcState
+                + ",updateProcState=" + mUpdateProcState + ",isForward=" + mIsForward + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index d62c507..6e49386 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -22,7 +22,6 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to stopped state.
@@ -35,32 +34,28 @@
     private final boolean mShowWindow;
     private final int mConfigChanges;
 
-    private int mLifecycleSeq;
-
     public StopActivityItem(boolean showWindow, int configChanges) {
         mShowWindow = showWindow;
         mConfigChanges = configChanges;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Stop transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
-        }
-    }
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-        client.handleStopActivity(token, mShowWindow, mConfigChanges, mLifecycleSeq);
+        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        client.reportStop(pendingActions);
+    }
+
+    @Override
     public int getTargetState() {
-        return STOPPED;
+        return ON_STOP;
     }
 
 
@@ -109,4 +104,10 @@
         result = 31 * result + mConfigChanges;
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "StopActivityItem{showWindow=" + mShowWindow + ",configChanges=" + mConfigChanges
+                + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
new file mode 100644
index 0000000..bd24824
--- /dev/null
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.util.IntArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Class that manages transaction execution in the correct order.
+ * @hide
+ */
+public class TransactionExecutor {
+
+    private static final boolean DEBUG_RESOLVER = false;
+    private static final String TAG = "TransactionExecutor";
+
+    private ClientTransactionHandler mTransactionHandler;
+    private PendingTransactionActions mPendingActions = new PendingTransactionActions();
+
+    // Temp holder for lifecycle path.
+    // No direct transition between two states should take more than one complete cycle of 6 states.
+    @ActivityLifecycleItem.LifecycleState
+    private IntArray mLifecycleSequence = new IntArray(6);
+
+    /** Initialize an instance with transaction handler, that will execute all requested actions. */
+    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
+        mTransactionHandler = clientTransactionHandler;
+    }
+
+    /**
+     * Resolve transaction.
+     * First all callbacks will be executed in the order they appear in the list. If a callback
+     * requires a certain pre- or post-execution state, the client will be transitioned accordingly.
+     * Then the client will cycle to the final lifecycle state if provided. Otherwise, it will
+     * either remain in the initial state, or last state needed by a callback.
+     */
+    public void execute(ClientTransaction transaction) {
+        final IBinder token = transaction.getActivityToken();
+        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
+
+        executeCallbacks(transaction);
+
+        executeLifecycleState(transaction);
+        mPendingActions.clear();
+        log("End resolving transaction");
+    }
+
+    /** Cycle through all states requested by callbacks and execute them at proper times. */
+    @VisibleForTesting
+    public void executeCallbacks(ClientTransaction transaction) {
+        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
+        if (callbacks == null) {
+            // No callbacks to execute, return early.
+            return;
+        }
+        log("Resolving callbacks");
+
+        final IBinder token = transaction.getActivityToken();
+        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+        final int size = callbacks.size();
+        for (int i = 0; i < size; ++i) {
+            final ClientTransactionItem item = callbacks.get(i);
+            log("Resolving callback: " + item);
+            final int preExecutionState = item.getPreExecutionState();
+            if (preExecutionState != UNDEFINED) {
+                cycleToPath(r, preExecutionState);
+            }
+
+            item.execute(mTransactionHandler, token, mPendingActions);
+            item.postExecute(mTransactionHandler, token, mPendingActions);
+            if (r == null) {
+                // Launch activity request will create an activity record.
+                r = mTransactionHandler.getActivityClient(token);
+            }
+
+            final int postExecutionState = item.getPostExecutionState();
+            if (postExecutionState != UNDEFINED) {
+                cycleToPath(r, postExecutionState);
+            }
+        }
+    }
+
+    /** Transition to the final state if requested by the transaction. */
+    private void executeLifecycleState(ClientTransaction transaction) {
+        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
+        if (lifecycleItem == null) {
+            // No lifecycle request, return early.
+            return;
+        }
+        log("Resolving lifecycle state: " + lifecycleItem);
+
+        final IBinder token = transaction.getActivityToken();
+        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
+        // Cycle to the state right before the final requested state.
+        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
+
+        // Execute the final transition with proper parameters.
+        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
+        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
+    }
+
+    /** Transition the client between states. */
+    @VisibleForTesting
+    public void cycleToPath(ActivityClientRecord r, int finish) {
+        cycleToPath(r, finish, false /* excludeLastState */);
+    }
+
+    /**
+     * Transition the client between states with an option not to perform the last hop in the
+     * sequence. This is used when resolving lifecycle state request, when the last transition must
+     * be performed with some specific parameters.
+     */
+    private void cycleToPath(ActivityClientRecord r, int finish,
+            boolean excludeLastState) {
+        final int start = r.getLifecycleState();
+        log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
+        initLifecyclePath(start, finish, excludeLastState);
+        performLifecycleSequence(r);
+    }
+
+    /** Transition the client through previously initialized state sequence. */
+    private void performLifecycleSequence(ActivityClientRecord r) {
+        final int size = mLifecycleSequence.size();
+        for (int i = 0, state; i < size; i++) {
+            state = mLifecycleSequence.get(i);
+            log("Transitioning to state: " + state);
+            switch (state) {
+                case ON_CREATE:
+                    mTransactionHandler.handleLaunchActivity(r, mPendingActions);
+                    break;
+                case ON_START:
+                    mTransactionHandler.handleStartActivity(r, mPendingActions);
+                    break;
+                case ON_RESUME:
+                    mTransactionHandler.handleResumeActivity(r.token, false /* clearHide */,
+                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
+                    break;
+                case ON_PAUSE:
+                    mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
+                            false /* userLeaving */, 0 /* configChanges */,
+                            true /* dontReport */, mPendingActions);
+                    break;
+                case ON_STOP:
+                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
+                            0 /* configChanges */, mPendingActions);
+                    break;
+                case ON_DESTROY:
+                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
+                            0 /* configChanges */, false /* getNonConfigInstance */);
+                    break;
+                case ON_RESTART:
+                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
+            }
+        }
+    }
+
+    /**
+     * Calculate the path through main lifecycle states for an activity and fill
+     * @link #mLifecycleSequence} with values starting with the state that follows the initial
+     * state.
+     */
+    public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
+        mLifecycleSequence.clear();
+        if (finish >= start) {
+            // just go there
+            for (int i = start + 1; i <= finish; i++) {
+                mLifecycleSequence.add(i);
+            }
+        } else { // finish < start, can't just cycle down
+            if (start == ON_PAUSE && finish == ON_RESUME) {
+                // Special case when we can just directly go to resumed state.
+                mLifecycleSequence.add(ON_RESUME);
+            } else if (start <= ON_STOP && finish >= ON_START) {
+                // Restart and go to required state.
+
+                // Go to stopped state first.
+                for (int i = start + 1; i <= ON_STOP; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Restart
+                mLifecycleSequence.add(ON_RESTART);
+                // Go to required state
+                for (int i = ON_START; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            } else {
+                // Relaunch and go to required state
+
+                // Go to destroyed state first.
+                for (int i = start + 1; i <= ON_DESTROY; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Go to required state
+                for (int i = ON_CREATE; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            }
+        }
+
+        // Remove last transition in case we want to perform it with some specific params.
+        if (excludeLastState && mLifecycleSequence.size() != 0) {
+            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
+        }
+    }
+
+    @VisibleForTesting
+    public int[] getLifecycleSequence() {
+        return mLifecycleSequence.toArray();
+    }
+
+    private static void log(String message) {
+        if (DEBUG_RESOLVER) {
+            Slog.d(TAG, message);
+        }
+    }
+}
diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java
index 8e88b38..6fcdcfa 100644
--- a/core/java/android/app/servertransaction/WindowVisibilityItem.java
+++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Trace;
@@ -35,7 +36,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
         client.handleWindowVisibility(token, mShowWindow);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -82,4 +84,9 @@
     public int hashCode() {
         return 17 + 31 * (mShowWindow ? 1 : 0);
     }
+
+    @Override
+    public String toString() {
+        return "WindowVisibilityItem{showWindow=" + mShowWindow + "}";
+    }
 }
diff --git a/core/java/android/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/content/Intent.java b/core/java/android/content/Intent.java
index 28bd928..6fb1afd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4915,8 +4915,9 @@
      * <li>Enumeration of features here is not meant to restrict capabilities of the quick viewer.
      * Quick viewer can implement features not listed below.
      * <li>Features included at this time are: {@link QuickViewConstants#FEATURE_VIEW},
-     * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DOWNLOAD},
-     * {@link QuickViewConstants#FEATURE_SEND}, {@link QuickViewConstants#FEATURE_PRINT}.
+     * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DELETE},
+     * {@link QuickViewConstants#FEATURE_DOWNLOAD}, {@link QuickViewConstants#FEATURE_SEND},
+     * {@link QuickViewConstants#FEATURE_PRINT}.
      * <p>
      * Requirements:
      * <li>Quick viewer shouldn't show a feature if the feature is absent in
diff --git a/core/java/android/content/QuickViewConstants.java b/core/java/android/content/QuickViewConstants.java
index 7455d0c..a25513d 100644
--- a/core/java/android/content/QuickViewConstants.java
+++ b/core/java/android/content/QuickViewConstants.java
@@ -33,7 +33,7 @@
     public static final String FEATURE_VIEW = "android:view";
 
     /**
-     * Feature to view a document using system standard editing mechanism, like
+     * Feature to edit a document using system standard editing mechanism, like
      * {@link Intent#ACTION_EDIT}.
      *
      * @see Intent#EXTRA_QUICK_VIEW_FEATURES
@@ -42,6 +42,15 @@
     public static final String FEATURE_EDIT = "android:edit";
 
     /**
+     * Feature to delete an individual document. Quick viewer implementations must use
+     * Storage Access Framework to both verify delete permission and to delete content.
+     *
+     * @see DocumentsContract#Document#FLAG_SUPPORTS_DELETE
+     * @see DocumentsContract#deleteDocument(ContentResolver resolver, Uri documentUri)
+     */
+    public static final String FEATURE_DELETE = "android:delete";
+
+    /**
      * Feature to view a document using system standard sending mechanism, like
      * {@link Intent#ACTION_SEND}.
      *
diff --git a/core/java/android/content/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/os/Environment.java b/core/java/android/os/Environment.java
index 1745121..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.
@@ -609,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/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/provider/CallLog.java b/core/java/android/provider/CallLog.java
index a8acb97..766ad84 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -224,6 +224,12 @@
         public static final int FEATURES_WIFI = 0x8;
 
         /**
+         * Indicates the call underwent Assisted Dialing.
+         * @hide
+         */
+        public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+
+        /**
          * The phone number as the user entered it.
          * <P>Type: TEXT</P>
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e62576c..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)
@@ -11123,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/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 74866b8..f658ae0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -46,6 +46,7 @@
 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;
@@ -65,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;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd0ae33..e5ab3e1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.IWindowSession;
 import android.view.InputChannel;
@@ -176,6 +177,9 @@
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
         final Rect mBackdropFrame = new Rect();
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+        DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -302,7 +306,8 @@
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                     Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                     MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
-                    boolean alwaysConsumeNavBar, int displayId) {
+                    boolean alwaysConsumeNavBar, int displayId,
+                    DisplayCutout.ParcelableWrapper displayCutout) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0, outsets);
                 mCaller.sendMessage(msg);
@@ -750,7 +755,7 @@
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                             Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
-                                mInputChannel) < 0) {
+                                mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -776,7 +781,7 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -800,6 +805,8 @@
                         mStableInsets.top += padding.top;
                         mStableInsets.right += padding.right;
                         mStableInsets.bottom += padding.bottom;
+                        mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top,
+                                -padding.right, -padding.bottom));
                     }
 
                     if (mCurWidth != w) {
@@ -819,6 +826,7 @@
                     insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
                     insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
                     insetsChanged |= !mDispatchedOutsets.equals(mOutsets);
+                    insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get());
 
                     mSurfaceHolder.setSurfaceFrameSize(w, h);
                     mSurfaceHolder.mSurfaceLock.unlock();
@@ -885,12 +893,13 @@
                             mDispatchedContentInsets.set(mContentInsets);
                             mDispatchedStableInsets.set(mStableInsets);
                             mDispatchedOutsets.set(mOutsets);
+                            mDispatchedDisplayCutout = mDisplayCutout.get();
                             mFinalSystemInsets.set(mDispatchedOverscanInsets);
                             mFinalStableInsets.set(mDispatchedStableInsets);
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
                                     null, mFinalStableInsets,
                                     getResources().getConfiguration().isScreenRound(), false,
-                                    null /* displayCutout */);
+                                    mDispatchedDisplayCutout);
                             if (DEBUG) {
                                 Log.v(TAG, "dispatching insets=" + insets);
                             }
diff --git a/core/java/android/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/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 2ffa1c5..ba6b6cf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -164,6 +164,7 @@
     private long mLastFrameTimeNanos;
     private long mFrameIntervalNanos;
     private boolean mDebugPrintNextFrameTimeDelta;
+    private int mFPSDivisor = 1;
 
     /**
      * Contains information about the current frame for jank-tracking,
@@ -601,6 +602,11 @@
         }
     }
 
+    void setFPSDivisor(int divisor) {
+        if (divisor <= 0) divisor = 1;
+        mFPSDivisor = divisor;
+    }
+
     void doFrame(long frameTimeNanos, int frame) {
         final long startNanos;
         synchronized (mLock) {
@@ -643,6 +649,14 @@
                 return;
             }
 
+            if (mFPSDivisor > 1) {
+                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
+                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
+                    scheduleVsyncLocked();
+                    return;
+                }
+            }
+
             mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
             mFrameScheduled = false;
             mLastFrameTimeNanos = frameTimeNanos;
diff --git a/core/java/android/view/DisplayCutout.aidl b/core/java/android/view/DisplayCutout.aidl
new file mode 100644
index 0000000..6d13b99
--- /dev/null
+++ b/core/java/android/view/DisplayCutout.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable DisplayCutout.ParcelableWrapper;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 611cc63..07a57fb 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -23,6 +23,7 @@
 import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.DisplayCutout;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -50,7 +51,8 @@
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
             in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
-            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
+            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            in DisplayCutout.ParcelableWrapper displayCutout);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 29032a7..9e103a3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -147,7 +147,6 @@
     void exitKeyguardSecurely(IOnKeyguardExitResult callback);
     boolean isKeyguardLocked();
     boolean isKeyguardSecure();
-    boolean inKeyguardRestrictedInputMode();
     void dismissKeyguard(IKeyguardDismissCallback callback);
 
     // Requires INTERACT_ACROSS_USERS_FULL permission
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 51d6514..ed167c8 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,6 +22,7 @@
 import android.graphics.Region;
 import android.os.Bundle;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.InputChannel;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -40,7 +41,8 @@
             out InputChannel outInputChannel);
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
+            out Rect outStableInsets, out Rect outOutsets,
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
     int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
@@ -96,6 +98,7 @@
             int flags, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
+            out DisplayCutout.ParcelableWrapper displayCutout,
             out MergedConfiguration outMergedConfiguration, out Surface outSurface);
 
     /*
diff --git a/core/java/android/view/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 345e300..623759e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -893,6 +893,15 @@
      */
     private static boolean sAutoFocusableOffUIThreadWontNotifyParents;
 
+    /**
+     * Prior to P things like setScaleX() allowed passing float values that were bogus such as
+     * Float.NaN. If the app is targetting P or later then passing these values will result in an
+     * exception being thrown. If the app is targetting an earlier SDK version, then we will
+     * silently clamp these values to avoid crashes elsewhere when the rendering code hits
+     * these bogus values.
+     */
+    private static boolean sThrowOnInvalidFloatProperties;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -4752,6 +4761,8 @@
             sUseDefaultFocusHighlight = context.getResources().getBoolean(
                     com.android.internal.R.bool.config_useDefaultFocusHighlight);
 
+            sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P;
+
             sCompatibilityDone = true;
         }
     }
@@ -14272,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);
@@ -14309,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);
@@ -14859,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);
     }
 
     /**
@@ -14954,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);
@@ -15047,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);
@@ -25718,6 +25757,9 @@
          */
         final Rect mStableInsets = new Rect();
 
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+
         /**
          * For windows that include areas that are not covered by real surface these are the outsets
          * for real surface.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c9d863..0bae36b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -384,12 +384,15 @@
     final Rect mPendingContentInsets = new Rect();
     final Rect mPendingOutsets = new Rect();
     final Rect mPendingBackDropFrame = new Rect();
+    final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
+            new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
     final Rect mDispatchContentInsets = new Rect();
     final Rect mDispatchStableInsets = new Rect();
+    DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
 
     private WindowInsets mLastWindowInsets;
 
@@ -730,7 +733,7 @@
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(),
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -752,6 +755,7 @@
                 mPendingOverscanInsets.set(0, 0, 0, 0);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingStableInsets.set(mAttachInfo.mStableInsets);
+                mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
@@ -1544,15 +1548,20 @@
         if (mLastWindowInsets == null || forceConstruct) {
             mDispatchContentInsets.set(mAttachInfo.mContentInsets);
             mDispatchStableInsets.set(mAttachInfo.mStableInsets);
+            mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
+
             Rect contentInsets = mDispatchContentInsets;
             Rect stableInsets = mDispatchStableInsets;
+            DisplayCutout displayCutout = mDispatchDisplayCutout;
             // For dispatch we preserve old logic, but for direct requests from Views we allow to
             // immediately use pending insets.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
-                        !mPendingStableInsets.equals(stableInsets))) {
+                        !mPendingStableInsets.equals(stableInsets) ||
+                        !mPendingDisplayCutout.get().equals(displayCutout))) {
                 contentInsets = mPendingContentInsets;
                 stableInsets = mPendingStableInsets;
+                displayCutout = mPendingDisplayCutout.get();
             }
             Rect outsets = mAttachInfo.mOutsets;
             if (outsets.left > 0 || outsets.top > 0 || outsets.right > 0 || outsets.bottom > 0) {
@@ -1563,7 +1572,7 @@
             mLastWindowInsets = new WindowInsets(contentInsets,
                     null /* windowDecorInsets */, stableInsets,
                     mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, null /* displayCutout */);
+                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
         }
         return mLastWindowInsets;
     }
@@ -1730,6 +1739,9 @@
                 if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                     insetsChanged = true;
                 }
+                if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
+                    insetsChanged = true;
+                }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
@@ -1906,7 +1918,8 @@
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
-                        + " visible=" + mPendingStableInsets.toShortString()
+                        + " stable=" + mPendingStableInsets.toShortString()
+                        + " cutout=" + mPendingDisplayCutout.get().toString()
                         + " outsets=" + mPendingOutsets.toShortString()
                         + " surface=" + mSurface);
 
@@ -1931,6 +1944,8 @@
                         mAttachInfo.mVisibleInsets);
                 final boolean stableInsetsChanged = !mPendingStableInsets.equals(
                         mAttachInfo.mStableInsets);
+                final boolean cutoutChanged = !mPendingDisplayCutout.equals(
+                        mAttachInfo.mDisplayCutout);
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -1955,6 +1970,14 @@
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
+                if (cutoutChanged) {
+                    mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
+                    if (DEBUG_LAYOUT) {
+                        Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
+                    }
+                    // Need to relayout with content insets.
+                    contentInsetsChanged = true;
+                }
                 if (alwaysConsumeNavBarChanged) {
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
@@ -2056,6 +2079,7 @@
                         mResizeMode = freeformResizing
                                 ? RESIZE_MODE_FREEFORM
                                 : RESIZE_MODE_DOCKED_DIVIDER;
+                        // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame,
                                 mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
                                 mPendingStableInsets, mResizeMode);
@@ -3776,6 +3800,7 @@
                             && mPendingOverscanInsets.equals(args.arg5)
                             && mPendingContentInsets.equals(args.arg2)
                             && mPendingStableInsets.equals(args.arg6)
+                            && mPendingDisplayCutout.get().equals(args.arg9)
                             && mPendingVisibleInsets.equals(args.arg3)
                             && mPendingOutsets.equals(args.arg7)
                             && mPendingBackDropFrame.equals(args.arg8)
@@ -3808,6 +3833,7 @@
                                 || !mPendingOverscanInsets.equals(args.arg5)
                                 || !mPendingContentInsets.equals(args.arg2)
                                 || !mPendingStableInsets.equals(args.arg6)
+                                || !mPendingDisplayCutout.get().equals(args.arg9)
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
@@ -3815,6 +3841,7 @@
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
+                        mPendingDisplayCutout.set((DisplayCutout) args.arg9);
                         mPendingVisibleInsets.set((Rect) args.arg3);
                         mPendingOutsets.set((Rect) args.arg7);
                         mPendingBackDropFrame.set((Rect) args.arg8);
@@ -6258,7 +6285,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
+                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurface);
 
         mPendingAlwaysConsumeNavBar =
@@ -6541,7 +6568,8 @@
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -6578,6 +6606,7 @@
         args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
         args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
+        args.arg9 = displayCutout.get(); // DisplayCutout is immutable.
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeNavBar ? 1 : 0;
         args.argi3 = displayId;
@@ -7610,12 +7639,13 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                         visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
-                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
+                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId, displayCutout);
             }
         }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9a99e53..419aeb3 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -47,6 +47,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -428,7 +429,7 @@
      * @hide
      */
     public AutofillManager(Context context, IAutoFillManager service) {
-        mContext = context;
+        mContext = Preconditions.checkNotNull(context, "context cannot be null");
         mService = service;
     }
 
@@ -457,7 +458,7 @@
             if (mSessionId != NO_SESSION) {
                 ensureServiceClientAddedIfNeededLocked();
 
-                final AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
                 if (client != null) {
                     try {
                         final boolean sessionWasRestored = mService.restoreSession(mSessionId,
@@ -1057,6 +1058,23 @@
     }
 
     /**
+     * TODO(b/67867469):
+     * - proper javadoc
+     * - mention this method in other places
+     * - unhide / remove testApi
+     * @hide
+     */
+    @TestApi
+    public boolean isFieldClassificationEnabled() {
+        try {
+            return mService.isFieldClassificationEnabled();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
      * Returns {@code true} if autofill is supported by the current device and
      * is supported for this user.
      *
@@ -1076,7 +1094,8 @@
         }
     }
 
-    private AutofillClient getClientLocked() {
+    // Note: don't need to use locked suffix because mContext is final.
+    private AutofillClient getClient() {
         final AutofillClient client = mContext.getAutofillClient();
         if (client == null && sDebug) {
             Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
@@ -1139,16 +1158,16 @@
             return;
         }
         try {
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
+            if (client == null) return; // NOTE: getClient() already logd it..
+
             mSessionId = mService.startSession(mContext.getActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, client.getComponentName());
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
             }
-            if (client != null) {
-                client.autofillCallbackResetableStateAvailable();
-            }
+            client.autofillCallbackResetableStateAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1200,7 +1219,9 @@
 
         try {
             if (restartIfNecessary) {
-                final AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
+                if (client == null) return; // NOTE: getClient() already logd it..
+
                 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
                         mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                         mCallback != null, flags, client.getComponentName(), mSessionId, action);
@@ -1208,9 +1229,7 @@
                     if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
                     mSessionId = newId;
                     mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
-                    if (client != null) {
-                        client.autofillCallbackResetableStateAvailable();
-                    }
+                    client.autofillCallbackResetableStateAvailable();
                 }
             } else {
                 mService.updateSession(mSessionId, id, bounds, value, action, flags,
@@ -1223,7 +1242,7 @@
     }
 
     private void ensureServiceClientAddedIfNeededLocked() {
-        if (getClientLocked() == null) {
+        if (getClient() == null) {
             return;
         }
 
@@ -1306,7 +1325,7 @@
         AutofillCallback callback = null;
         synchronized (mLock) {
             if (mSessionId == sessionId) {
-                AutofillClient client = getClientLocked();
+                AutofillClient client = getClient();
 
                 if (client != null) {
                     if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
@@ -1331,7 +1350,7 @@
             Intent fillInIntent) {
         synchronized (mLock) {
             if (sessionId == mSessionId) {
-                AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
                 if (client != null) {
                     client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
                 }
@@ -1396,7 +1415,7 @@
                 return;
             }
 
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
             if (client == null) {
                 return;
             }
@@ -1573,7 +1592,7 @@
             // 1. If local and remote session id are off sync the UI would be stuck shown
             // 2. There is a race between the user state being destroyed due the fill
             //    service being uninstalled and the UI being dismissed.
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
             if (client != null) {
                 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
                     callback = mCallback;
@@ -1603,7 +1622,7 @@
 
         AutofillCallback callback = null;
         synchronized (mLock) {
-            if (mSessionId == sessionId && getClientLocked() != null) {
+            if (mSessionId == sessionId && getClient() != null) {
                 callback = mCallback;
             }
         }
@@ -1660,7 +1679,7 @@
      * @return The view or {@code null} if view was not found
      */
     private View findView(@NonNull AutofillId autofillId) {
-        final AutofillClient client = getClientLocked();
+        final AutofillClient client = getClient();
 
         if (client == null) {
             return null;
@@ -1694,7 +1713,7 @@
         pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
         pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
         pw.print(pfx); pw.print("context: "); pw.println(mContext);
-        pw.print(pfx); pw.print("client: "); pw.println(getClientLocked());
+        pw.print(pfx); pw.print("client: "); pw.println(getClient());
         pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
         pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
         pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1741,7 +1760,7 @@
     }
 
     private void post(Runnable runnable) {
-        final AutofillClient client = getClientLocked();
+        final AutofillClient client = getClient();
         if (client == null) {
             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
             return;
@@ -1824,7 +1843,7 @@
          * @param trackedIds The views to be tracked
          */
         TrackedViews(@Nullable AutofillId[] trackedIds) {
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
             if (trackedIds != null && client != null) {
                 final boolean[] isVisible;
 
@@ -1865,7 +1884,7 @@
          * @param isVisible visible if the view is visible in the view hierarchy.
          */
         void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
 
             if (sDebug) {
                 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
@@ -1902,7 +1921,7 @@
         void onVisibleForAutofillLocked() {
             // The visibility of the views might have changed while the client was not be visible,
             // hence update the visibility state for all views.
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
             ArraySet<AutofillId> updatedVisibleTrackedIds = null;
             ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
             if (client != null) {
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index b95704a..4b2c53c 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -18,11 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Bundle;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Set;
 
 /** @hide */
 public final class Helper {
@@ -31,37 +26,20 @@
     public static boolean sDebug = false;
     public static boolean sVerbose = false;
 
-    public static final String REDACTED = "[REDACTED]";
-
-    static StringBuilder append(StringBuilder builder, Bundle bundle) {
-        if (bundle == null || !sDebug) {
-            builder.append("N/A");
-        } else if (!sVerbose) {
-            builder.append(REDACTED);
-        } else {
-            final Set<String> keySet = bundle.keySet();
-            builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
-            for (String key : keySet) {
-                final Object value = bundle.get(key);
-                builder.append(' ').append(key).append('=');
-                builder.append((value instanceof Object[])
-                        ? Arrays.toString((Objects[]) value) : value);
-            }
-            builder.append(']');
-        }
-        return builder;
-    }
-
     /**
      * Appends {@code value} to the {@code builder} redacting its contents.
      */
     public static void appendRedacted(@NonNull StringBuilder builder,
             @Nullable CharSequence value) {
-        if (value == null) {
-            builder.append("null");
-        } else {
-            builder.append(value.length()).append("_chars");
-        }
+        builder.append(getRedacted(value));
+    }
+
+    /**
+     * Gets the redacted version of a value.
+     */
+    @NonNull
+    public static String getRedacted(@Nullable CharSequence value) {
+        return (value == null) ? "null" : value.length() + "_chars";
     }
 
     /**
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 7d6a19f..f49aa5b 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -56,4 +56,5 @@
     void onPendingSaveUi(int operation, IBinder token);
     UserData getUserData();
     void setUserData(in UserData userData);
+    boolean isFieldClassificationEnabled();
 }
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 1279040..7156300 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -570,11 +570,12 @@
                 mFormatChangeObserver = new FormatChangeObserver(getHandler());
             }
             final ContentResolver resolver = getContext().getContentResolver();
+            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
             if (mShowCurrentUserTime) {
-                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                resolver.registerContentObserver(uri, true,
                         mFormatChangeObserver, UserHandle.USER_ALL);
             } else {
-                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                resolver.registerContentObserver(uri, true,
                         mFormatChangeObserver);
             }
         }
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 8fb56d4..d9aa325 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -48,6 +48,7 @@
     public Object arg6;
     public Object arg7;
     public Object arg8;
+    public Object arg9;
     public int argi1;
     public int argi2;
     public int argi3;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 361fd3da..7178a0d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -22,6 +22,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -41,7 +42,8 @@
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index b479cb1..d7b9132 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -108,11 +108,19 @@
      * Enables or disables rotation lock from the system UI toggle.
      */
     public static void setRotationLock(Context context, final boolean enabled) {
+        final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
+        setRotationLockAtAngle(context, enabled, rotation);
+    }
+
+    /**
+     * Enables or disables rotation lock at a specific rotation from system UI.
+     */
+    public static void setRotationLockAtAngle(Context context, final boolean enabled,
+            final int rotation) {
         Settings.System.putIntForUser(context.getContentResolver(),
                 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
                 UserHandle.USER_CURRENT);
 
-        final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
         setRotationLock(enabled, rotation);
     }
 
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e9ea702..6f4a58a 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -96,6 +96,11 @@
     tree->setAllowCaching(allowCaching);
 }
 
+static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    tree->setAntiAlias(aa);
+}
+
 /**
  * Draw
  */
@@ -363,6 +368,7 @@
         {"nSetRendererViewportSize", "(JFF)V", (void*)setTreeViewportSize},
         {"nSetRootAlpha", "(JF)Z", (void*)setRootAlpha},
         {"nGetRootAlpha", "(J)F", (void*)getRootAlpha},
+        {"nSetAntiAlias", "(JZ)V", (void*)setAntiAlias},
         {"nSetAllowCaching", "(JZ)V", (void*)setAllowCaching},
 
         {"nCreateFullPath", "()J", (void*)createEmptyFullPath},
diff --git a/core/jni/android_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/strings.xml b/core/res/res/values/strings.xml
index 2d40490..ef8f6af 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4474,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 cf19536..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" />
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 d42d2ac..47990a1 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -45,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/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index e0fcf08..ea2dd59 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -22,6 +22,7 @@
 
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -30,11 +31,11 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-// TODO(lifecycler): Add to presubmit after checking for flakiness.
+@Presubmit
 public class ClientTransactionTests {
 
     @Test
-    public void testPrepare() {
+    public void testPreExecute() {
         ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
         ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
@@ -47,32 +48,10 @@
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(stateRequest);
 
-        transaction.prepare(clientTransactionHandler);
+        transaction.preExecute(clientTransactionHandler);
 
-        verify(callback1, times(1)).prepare(clientTransactionHandler, token);
-        verify(callback2, times(1)).prepare(clientTransactionHandler, token);
-        verify(stateRequest, times(1)).prepare(clientTransactionHandler, token);
-    }
-
-    @Test
-    public void testExecute() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        IBinder token = mock(IBinder.class);
-
-        ClientTransaction transaction = new ClientTransaction(null /* client */,
-                token /* activityToken */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(stateRequest);
-
-        ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
-        transaction.prepare(clientTransactionHandler);
-        transaction.execute(clientTransactionHandler);
-
-        verify(callback1, times(1)).execute(clientTransactionHandler, token);
-        verify(callback2, times(1)).execute(clientTransactionHandler, token);
-        verify(stateRequest, times(1)).execute(clientTransactionHandler, token);
+        verify(callback1, times(1)).preExecute(clientTransactionHandler, token);
+        verify(callback2, times(1)).preExecute(clientTransactionHandler, token);
+        verify(stateRequest, times(1)).preExecute(clientTransactionHandler, token);
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
new file mode 100644
index 0000000..d283004
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+
+/** Test {@link TransactionExecutor} logic. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TransactionExecutorTests {
+
+    private TransactionExecutor mExecutor;
+    private ClientTransactionHandler mTransactionHandler;
+    private ActivityClientRecord mClientRecord;
+
+    @Before
+    public void setUp() throws Exception {
+        mTransactionHandler = mock(ClientTransactionHandler.class);
+
+        mClientRecord = new ActivityClientRecord();
+        when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
+
+        mExecutor = spy(new TransactionExecutor(mTransactionHandler));
+    }
+
+    @Test
+    public void testLifecycleFromPreOnCreate() {
+        mClientRecord.setState(PRE_ON_CREATE);
+        assertArrayEquals(new int[] {}, path(PRE_ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                path(ON_STOP));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
+                path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnCreate() {
+        mClientRecord.setState(ON_CREATE);
+        assertArrayEquals(new int[] {}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
+                path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnStart() {
+        mClientRecord.setState(ON_START);
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE},
+                path(ON_CREATE));
+        assertArrayEquals(new int[] {}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnResume() {
+        mClientRecord.setState(ON_RESUME);
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnPause() {
+        mClientRecord.setState(ON_PAUSE);
+        assertArrayEquals(new int[] {ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_STOP, ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnStop() {
+        mClientRecord.setState(ON_STOP);
+        assertArrayEquals(new int[] {ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnDestroy() {
+        mClientRecord.setState(ON_DESTROY);
+        assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                path(ON_STOP));
+        assertArrayEquals(new int[] {}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleExcludeLastItem() {
+        mClientRecord.setState(PRE_ON_CREATE);
+        assertArrayEquals(new int[] {}, pathExcludeLast(PRE_ON_CREATE));
+        assertArrayEquals(new int[] {}, pathExcludeLast(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE}, pathExcludeLast(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, pathExcludeLast(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, pathExcludeLast(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE},
+                pathExcludeLast(ON_STOP));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                pathExcludeLast(ON_DESTROY));
+    }
+
+    @Test
+    public void testTransactionResolution() {
+        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+        IBinder token = mock(IBinder.class);
+
+        ClientTransaction transaction = new ClientTransaction(null /* client */,
+                token /* activityToken */);
+        transaction.addCallback(callback1);
+        transaction.addCallback(callback2);
+        transaction.setLifecycleStateRequest(stateRequest);
+
+        transaction.preExecute(mTransactionHandler);
+        mExecutor.execute(transaction);
+
+        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
+        inOrder.verify(callback1, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+        inOrder.verify(callback2, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+        inOrder.verify(stateRequest, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+    }
+
+    @Test
+    public void testRequiredStateResolution() {
+        ActivityResultItem activityResultItem = new ActivityResultItem(new ArrayList<>());
+
+        IBinder token = mock(IBinder.class);
+        ClientTransaction transaction = new ClientTransaction(null /* client */,
+                token /* activityToken */);
+        transaction.addCallback(activityResultItem);
+
+        mExecutor.executeCallbacks(transaction);
+
+        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
+    }
+
+    private int[] path(int finish) {
+        mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
+                false /* excludeLastState */);
+        return mExecutor.getLifecycleSequence();
+    }
+
+    private int[] pathExcludeLast(int finish) {
+        mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
+                true /* excludeLastState */);
+        return mExecutor.getLifecycleSequence();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 9db7550..f6c656d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -46,6 +46,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -63,7 +64,7 @@
 /** Test parcelling and unparcelling of transactions and transaction items. */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-// TODO(lifecycler): Add to presubmit after checking for flakiness.
+@Presubmit
 public class TransactionParcelTests {
 
     private Parcel mParcel;
@@ -230,7 +231,7 @@
         LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo,
                 config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
                 procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(),
-                true /* notResumed */, true /* isForward */, null /* profilerInfo */);
+                true /* isForward */, null /* profilerInfo */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
diff --git a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
new file mode 100644
index 0000000..80e4c02
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.crossprofile;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsTest {
+    private static final UserHandle PERSONAL_PROFILE = UserHandle.of(0);
+    private static final UserHandle MANAGED_PROFILE = UserHandle.of(10);
+    private static final String MY_PACKAGE = "my.package";
+
+    private List<UserHandle> mTargetProfiles;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ICrossProfileApps mService;
+    @Mock
+    private Resources mResources;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Drawable mDrawable;
+    private CrossProfileApps mCrossProfileApps;
+
+    @Before
+    public void initCrossProfileApps() {
+        mCrossProfileApps = new CrossProfileApps(mContext, mService);
+    }
+
+    @Before
+    public void mockContext() {
+        when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
+        when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+    }
+
+    @Before
+    public void mockResources() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getDrawable(anyInt(), nullable(Resources.Theme.class)))
+                .thenReturn(mDrawable);
+    }
+
+    @Before
+    public void initUsers() throws Exception {
+        when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
+        when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
+
+        mTargetProfiles = new ArrayList<>();
+        when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
+        verify(mResources).getString(R.string.managed_profile_label);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+        verify(mResources).getString(R.string.user_owner_label);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingLabel_securityException() {
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_account_circle, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingIcon_securityException() {
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+    }
+
+    private void setValidTargetProfile(UserHandle userHandle) {
+        mTargetProfiles.add(userHandle);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
new file mode 100644
index 0000000..5189df5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.os.Environment.HAS_ANDROID;
+import static android.os.Environment.HAS_DCIM;
+import static android.os.Environment.HAS_DOWNLOADS;
+import static android.os.Environment.HAS_OTHER;
+import static android.os.Environment.classifyExternalStorageDirectory;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class EnvironmentTest {
+    private File dir;
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        dir = getContext().getDir("testing", Context.MODE_PRIVATE);
+        FileUtils.deleteContents(dir);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(dir);
+    }
+
+    @Test
+    public void testClassify_empty() {
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyDirs() {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "January").mkdirs();
+        Environment.buildPath(dir, "Downloads").mkdirs();
+        Environment.buildPath(dir, "LOST.DIR").mkdirs();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyFactory() throws Exception {
+        Environment.buildPath(dir, "autorun.inf").createNewFile();
+        Environment.buildPath(dir, "LaunchU3.exe").createNewFile();
+        Environment.buildPath(dir, "LaunchPad.zip").createNewFile();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_photos() throws Exception {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "IMG_1024.JPG").createNewFile();
+        Environment.buildPath(dir, "Download").mkdirs();
+        Environment.buildPath(dir, "Download", "foobar.pdf").createNewFile();
+        assertEquals(HAS_DCIM | HAS_DOWNLOADS, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_other() throws Exception {
+        Environment.buildPath(dir, "Android").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example", "internal.dat").createNewFile();
+        Environment.buildPath(dir, "Linux").mkdirs();
+        Environment.buildPath(dir, "Linux", "install-amd64-minimal-20170907.iso").createNewFile();
+        assertEquals(HAS_ANDROID | HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_otherRoot() throws Exception {
+        Environment.buildPath(dir, "Taxes.pdf").createNewFile();
+        assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 3a751af..cd20192 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,15 +21,24 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 
 import libcore.io.IoUtils;
 
 import com.google.android.collect.Sets;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,8 +46,8 @@
 import java.util.Arrays;
 import java.util.HashSet;
 
-@MediumTest
-public class FileUtilsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
     private static final String TEST_DATA =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
@@ -47,10 +56,12 @@
     private File mCopyFile;
     private File mTarget;
 
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
         mTestFile = new File(mDir, "test.file");
         mCopyFile = new File(mDir, "copy.file");
@@ -59,14 +70,15 @@
         FileUtils.deleteContents(mTarget);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         IoUtils.deleteContents(mDir);
         FileUtils.deleteContents(mTarget);
     }
 
     // TODO: test setPermissions(), getPermissions()
 
+    @Test
     public void testCopyFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         assertFalse(mCopyFile.exists());
@@ -75,6 +87,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testCopyToFile() throws Exception {
         final String s = "Foo Bar";
         assertFalse(mCopyFile.exists());
@@ -83,6 +96,7 @@
         assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testIsFilenameSafe() throws Exception {
         assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
         assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -90,6 +104,7 @@
         assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
     }
 
+    @Test
     public void testReadTextFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
 
@@ -110,6 +125,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
     }
 
+    @Test
     public void testReadTextFileWithZeroLengthFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         new FileOutputStream(mTestFile).close();  // Zero out the file
@@ -120,6 +136,7 @@
         assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
     }
 
+    @Test
     public void testContains() throws Exception {
         assertTrue(FileUtils.contains(new File("/"), new File("/moo.txt")));
         assertTrue(FileUtils.contains(new File("/"), new File("/")));
@@ -137,11 +154,13 @@
         assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/sdcard.txt")));
     }
 
+    @Test
     public void testDeleteOlderEmptyDir() throws Exception {
         FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
         assertDirContents();
     }
 
+    @Test
     public void testDeleteOlderTypical() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -152,6 +171,7 @@
         assertDirContents("file1", "file2", "file3");
     }
 
+    @Test
     public void testDeleteOlderInFuture() throws Exception {
         touch("file1", -HOUR_IN_MILLIS);
         touch("file2", HOUR_IN_MILLIS);
@@ -166,6 +186,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testDeleteOlderOnlyAge() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -177,6 +198,7 @@
         assertDirContents("file1");
     }
 
+    @Test
     public void testDeleteOlderOnlyCount() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -188,6 +210,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testValidExtFilename() throws Exception {
         assertTrue(FileUtils.isValidExtFilename("a"));
         assertTrue(FileUtils.isValidExtFilename("foo.bar"));
@@ -208,6 +231,7 @@
         assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
     }
 
+    @Test
     public void testValidFatFilename() throws Exception {
         assertTrue(FileUtils.isValidFatFilename("a"));
         assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
@@ -233,6 +257,7 @@
         assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
     }
 
+    @Test
     public void testTrimFilename() throws Exception {
         assertEquals("short.txt", FileUtils.trimFilename("short.txt", 16));
         assertEquals("extrem...eme.txt", FileUtils.trimFilename("extremelylongfilename.txt", 16));
@@ -245,6 +270,7 @@
         assertEquals("a...z", FileUtils.trimFilename(unicode, 6));
     }
 
+    @Test
     public void testBuildUniqueFile_normal() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -263,6 +289,7 @@
                 FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
     }
 
+    @Test
     public void testBuildUniqueFile_unknown() throws Exception {
         assertNameEquals("test",
                 FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -275,6 +302,7 @@
         assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
     }
 
+    @Test
     public void testBuildUniqueFile_dir() throws Exception {
         assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
         new File(mTarget, "test").mkdir();
@@ -288,6 +316,7 @@
                 FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_increment() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -298,6 +327,7 @@
                 FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_mimeless() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -312,6 +342,7 @@
         assertNameEquals("test.foo (1).bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
     }
 
+    @Test
     public void testRoundStorageSize() throws Exception {
         final long M128 = 128000000L;
         final long M256 = 256000000L;
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0367275..77c6c3e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -420,7 +420,7 @@
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                  // TODO(b/67867469): Move autofill settings below to
                  // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
-                 Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION,
+                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
diff --git a/core/tests/coretests/src/android/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/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/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 7b2e21a..c71585f 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -896,6 +896,13 @@
         return mVectorState.getNativeRenderer();
     }
 
+    /**
+     * @hide
+     */
+    public void setAntiAlias(boolean aa) {
+        nSetAntiAlias(mVectorState.mNativeTree.get(), aa);
+    }
+
     static class VectorDrawableState extends ConstantState {
         // Variables below need to be copied (deep copy if applicable) for mutation.
         int[] mThemeAttrs;
@@ -2269,6 +2276,8 @@
     @FastNative
     private static native float nGetRootAlpha(long rendererPtr);
     @FastNative
+    private static native void nSetAntiAlias(long rendererPtr, boolean aa);
+    @FastNative
     private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
 
     @FastNative
diff --git a/keystore/java/android/security/AttestedKeyPair.java b/keystore/java/android/security/AttestedKeyPair.java
new file mode 100644
index 0000000..c6bff5c
--- /dev/null
+++ b/keystore/java/android/security/AttestedKeyPair.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import java.security.KeyPair;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The {@code AttestedKeyPair} class contains a {@code KeyPair} instance of
+ * keys generated by Keystore and owned by KeyChain, as well as an attestation
+ * record for the key.
+ *
+ * <p>Such keys can be obtained by calling
+ * {@link android.app.admin.DevicePolicyManager#generateKeyPair}.
+ */
+
+public final class AttestedKeyPair {
+    private final KeyPair mKeyPair;
+    private final Certificate[] mAttestationRecord;
+
+    /**
+     * @hide Only created by the platform, no need to expose as public API.
+     */
+    public AttestedKeyPair(KeyPair keyPair, Certificate[] attestationRecord) {
+        mKeyPair = keyPair;
+        mAttestationRecord = attestationRecord;
+    }
+
+    /**
+     * Returns the generated key pair associated with the attestation record
+     * in this instance.
+     */
+    public KeyPair getKeyPair() {
+        return mKeyPair;
+    }
+
+    /**
+     * Returns the attestation record for the key pair in this instance.
+     *
+     * The attestation record is a chain of certificates. The leaf certificate links to the public
+     * key of this key pair and other properties of the key or the device. If the key is in secure
+     * hardware, and if the secure hardware supports attestation, the leaf certificate will be
+     * signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will be
+     * rooted at an untrusted certificate.
+     *
+     * The attestation record could be for properties of the key, or include device identifiers.
+     *
+     * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge}
+     * and  <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the attestation record inside the certificate.
+     */
+    public List<Certificate> getAttestationRecord() {
+        if (mAttestationRecord == null) {
+            return new ArrayList();
+        }
+        return Arrays.asList(mAttestationRecord);
+    }
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 635432d..b4331b2 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
 package android.security;
 
 import android.content.pm.StringParceledListSlice;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
@@ -31,6 +32,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
+    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
 
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index ed40b77..87677d4 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -195,7 +195,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2",
  *                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
  *                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -219,7 +219,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build());
  * SecretKey key = keyGenerator.generateKey();
  * Mac mac = Mac.getInstance("HmacSHA256");
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
new file mode 100644
index 0000000..3fb7b4f
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+parcelable ParcelableKeyGenParameterSpec;
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
new file mode 100644
index 0000000..b15e0a2
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A parcelable version of KeyGenParameterSpec
+ * @hide only used for communicating with the DPMS.
+ */
+public final class ParcelableKeyGenParameterSpec implements Parcelable {
+    private static final int ALGORITHM_PARAMETER_SPEC_NONE = 1;
+    private static final int ALGORITHM_PARAMETER_SPEC_RSA = 2;
+    private static final int ALGORITHM_PARAMETER_SPEC_EC = 3;
+
+    private final KeyGenParameterSpec mSpec;
+
+    public ParcelableKeyGenParameterSpec(
+            KeyGenParameterSpec spec) {
+        mSpec = spec;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    private static void writeOptionalDate(Parcel out, Date date) {
+        if (date != null) {
+            out.writeBoolean(true);
+            out.writeLong(date.getTime());
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSpec.getKeystoreAlias());
+        out.writeInt(mSpec.getPurposes());
+        out.writeInt(mSpec.getUid());
+        out.writeInt(mSpec.getKeySize());
+
+        // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
+        AlgorithmParameterSpec algoSpec = mSpec.getAlgorithmParameterSpec();
+        if (algoSpec == null) {
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_NONE);
+        } else if (algoSpec instanceof RSAKeyGenParameterSpec) {
+            RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_RSA);
+            out.writeInt(rsaSpec.getKeysize());
+            out.writeByteArray(rsaSpec.getPublicExponent().toByteArray());
+        } else if (algoSpec instanceof ECGenParameterSpec) {
+            ECGenParameterSpec ecSpec = (ECGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_EC);
+            out.writeString(ecSpec.getName());
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %s", algoSpec.getClass()));
+        }
+        out.writeByteArray(mSpec.getCertificateSubject().getEncoded());
+        out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray());
+        writeOptionalDate(out, mSpec.getCertificateNotBefore());
+        writeOptionalDate(out, mSpec.getCertificateNotAfter());
+        writeOptionalDate(out, mSpec.getKeyValidityStart());
+        writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd());
+        writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd());
+        out.writeStringArray(mSpec.getDigests());
+        out.writeStringArray(mSpec.getEncryptionPaddings());
+        out.writeStringArray(mSpec.getSignaturePaddings());
+        out.writeStringArray(mSpec.getBlockModes());
+        out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
+        out.writeBoolean(mSpec.isUserAuthenticationRequired());
+        out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeByteArray(mSpec.getAttestationChallenge());
+        out.writeBoolean(mSpec.isUniqueIdIncluded());
+        out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
+        out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+    }
+
+    private static Date readDateOrNull(Parcel in) {
+        boolean hasDate = in.readBoolean();
+        if (hasDate) {
+            return new Date(in.readLong());
+        } else {
+            return null;
+        }
+    }
+
+    private ParcelableKeyGenParameterSpec(Parcel in) {
+        String keystoreAlias = in.readString();
+        int purposes = in.readInt();
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes);
+        builder.setUid(in.readInt());
+        builder.setKeySize(in.readInt());
+
+        int keySpecType = in.readInt();
+        AlgorithmParameterSpec algorithmSpec = null;
+        if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
+            algorithmSpec = null;
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_RSA) {
+            int rsaKeySize = in.readInt();
+            BigInteger publicExponent = new BigInteger(in.createByteArray());
+            algorithmSpec = new RSAKeyGenParameterSpec(rsaKeySize, publicExponent);
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_EC) {
+            String stdName = in.readString();
+            algorithmSpec = new ECGenParameterSpec(stdName);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %d", algorithmSpec));
+        }
+        builder.setAlgorithmParameterSpec(algorithmSpec);
+        builder.setCertificateSubject(new X500Principal(in.createByteArray()));
+        builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
+        builder.setCertificateNotBefore(readDateOrNull(in));
+        builder.setCertificateNotAfter(readDateOrNull(in));
+        builder.setKeyValidityStart(readDateOrNull(in));
+        builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
+        builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
+        builder.setDigests(in.createStringArray());
+        builder.setEncryptionPaddings(in.createStringArray());
+        builder.setSignaturePaddings(in.createStringArray());
+        builder.setBlockModes(in.createStringArray());
+        builder.setRandomizedEncryptionRequired(in.readBoolean());
+        builder.setUserAuthenticationRequired(in.readBoolean());
+        builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
+        builder.setAttestationChallenge(in.createByteArray());
+        builder.setUniqueIdIncluded(in.readBoolean());
+        builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
+        builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+        mSpec = builder.build();
+    }
+
+    public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
+        @Override
+        public ParcelableKeyGenParameterSpec createFromParcel(Parcel in) {
+            return new ParcelableKeyGenParameterSpec(in);
+        }
+
+        @Override
+        public ParcelableKeyGenParameterSpec[] newArray(int size) {
+            return new ParcelableKeyGenParameterSpec[size];
+        }
+    };
+
+    public KeyGenParameterSpec getSpec() {
+        return mSpec;
+    }
+}
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..51adde4
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    legacy-android-test
+
+LOCAL_PACKAGE_NAME := KeystoreTests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9bf2d0c
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.tests"
+        android:label="Tests for Keystore">
+    </instrumentation>
+</manifest>
+
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
new file mode 100644
index 0000000..73b489f
--- /dev/null
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.runner.AndroidJUnit4;
+import java.math.BigInteger;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class ParcelableKeyGenParameterSpecTest {
+    static final String ALIAS = "keystore-alias";
+    static final String ANOTHER_ALIAS = "another-keystore-alias";
+    static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+    static final int UID = 1230;
+    static final int KEYSIZE = 2048;
+    static final X500Principal SUBJECT = new X500Principal("CN=subject");
+    static final BigInteger SERIAL = new BigInteger("1234567890");
+    static final Date NOT_BEFORE = new Date(1511799590);
+    static final Date NOT_AFTER = new Date(1511899590);
+    static final Date KEY_VALIDITY_START = new Date(1511799591);
+    static final Date KEY_VALIDITY_FOR_ORIG_END = new Date(1511799593);
+    static final Date KEY_VALIDITY_FOR_CONSUMPTION_END = new Date(1511799594);
+    static final String DIGEST = KeyProperties.DIGEST_SHA256;
+    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
+    static final String SIGNATURE_PADDING = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+    static final int USER_AUTHENTICATION_DURATION = 300;
+    static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
+
+    KeyGenParameterSpec configureDefaultSpec() {
+        return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setUid(UID)
+                .setKeySize(KEYSIZE)
+                .setCertificateSubject(SUBJECT)
+                .setCertificateSerialNumber(SERIAL)
+                .setCertificateNotBefore(NOT_BEFORE)
+                .setCertificateNotAfter(NOT_AFTER)
+                .setKeyValidityStart(KEY_VALIDITY_START)
+                .setKeyValidityForOriginationEnd(KEY_VALIDITY_FOR_ORIG_END)
+                .setKeyValidityForConsumptionEnd(KEY_VALIDITY_FOR_CONSUMPTION_END)
+                .setDigests(DIGEST)
+                .setEncryptionPaddings(ENCRYPTION_PADDING)
+                .setSignaturePaddings(SIGNATURE_PADDING)
+                .setBlockModes(BLOCK_MODE)
+                .setRandomizedEncryptionRequired(true)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_DURATION)
+                .setAttestationChallenge(ATTESTATION_CHALLENGE)
+                .setUniqueIdIncluded(true)
+                .setUserAuthenticationValidWhileOnBody(true)
+                .setInvalidatedByBiometricEnrollment(true)
+                .build();
+    }
+
+    void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+        assertThat(spec.getKeystoreAlias(), is(alias));
+        assertThat(spec.getPurposes(), is(KEY_PURPOSES));
+        assertThat(spec.getUid(), is(uid));
+        assertThat(spec.getKeySize(), is(KEYSIZE));
+        assertThat(spec.getCertificateSubject(), is(SUBJECT));
+        assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
+        assertThat(spec.getCertificateNotBefore(), is(NOT_BEFORE));
+        assertThat(spec.getCertificateNotAfter(), is(NOT_AFTER));
+        assertThat(spec.getKeyValidityStart(), is(KEY_VALIDITY_START));
+        assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
+        assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
+        assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+        assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
+        assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
+        assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
+        assertThat(spec.isRandomizedEncryptionRequired(), is(true));
+        assertThat(spec.isUserAuthenticationRequired(), is(true));
+        assertThat(
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                is(USER_AUTHENTICATION_DURATION));
+        assertThat(spec.getAttestationChallenge(), is(ATTESTATION_CHALLENGE));
+        assertThat(spec.isUniqueIdIncluded(), is(true));
+        assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
+        assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+    }
+
+    private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
+        Parcel parcel = Parcel.obtain();
+        spec.writeToParcel(parcel, spec.describeContents());
+
+        parcel.setDataPosition(0);
+        return parcel;
+    }
+
+    @Test
+    public void testParcelingWithAllValues() {
+        ParcelableKeyGenParameterSpec spec =
+            new ParcelableKeyGenParameterSpec(configureDefaultSpec());
+        Parcel parcel = parcelForReading(spec);
+        ParcelableKeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
+        validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingWithNullValues() {
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR
+            .createFromParcel(parcel)
+            .getSpec();
+        assertThat(fromParcel.getKeystoreAlias(), is(ALIAS));
+        assertThat(fromParcel.getPurposes(), is(KEY_PURPOSES));
+        assertThat(fromParcel.getCertificateNotBefore(), is(new Date(0L)));
+        assertThat(fromParcel.getCertificateNotAfter(), is(new Date(2461449600000L)));
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingRSAAlgoParameter() {
+        RSAKeyGenParameterSpec rsaSpec =
+                new RSAKeyGenParameterSpec(2048, new BigInteger("5231123"));
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+            .setAlgorithmParameterSpec(rsaSpec)
+            .build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        RSAKeyGenParameterSpec parcelSpec =
+                (RSAKeyGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        // Compare individual fields as RSAKeyGenParameterSpec, on android, does not
+        // implement equals()
+        assertEquals(parcelSpec.getKeysize(), rsaSpec.getKeysize());
+        assertEquals(parcelSpec.getPublicExponent(), rsaSpec.getPublicExponent());
+    }
+
+    @Test
+    public void testParcelingECAlgoParameter() {
+        ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+                new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                        .setAlgorithmParameterSpec(ecSpec)
+                        .build());
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        // Compare individual fields as ECGenParameterSpec, on android, does not
+        // implement equals()
+        ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        assertEquals(parcelSpec.getName(), ecSpec.getName());
+    }
+}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d41db63..4243e7e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -59,6 +59,7 @@
 bool Properties::filterOutTestOverhead = false;
 bool Properties::disableVsync = false;
 bool Properties::skpCaptureEnabled = false;
+bool Properties::enableRTAnimations = true;
 
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9c30e4a..af4b694 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,6 +255,9 @@
 
     static bool skpCaptureEnabled;
 
+    // For experimentation b/68769804
+    ANDROID_API static bool enableRTAnimations;
+
     // Used for testing only to change the render pipeline.
     static void overrideRenderPipelineType(RenderPipelineType);
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index ce00488..f118e8d 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -166,7 +166,7 @@
 
     if (needsFill) {
         paint.setStyle(SkPaint::Style::kFill_Style);
-        paint.setAntiAlias(true);
+        paint.setAntiAlias(mAntiAlias);
         outCanvas->drawPath(renderPath, paint);
     }
 
@@ -182,7 +182,7 @@
     }
     if (needsStroke) {
         paint.setStyle(SkPaint::Style::kStroke_Style);
-        paint.setAntiAlias(true);
+        paint.setAntiAlias(mAntiAlias);
         paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin()));
         paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap()));
         paint.setStrokeMiter(properties.getStrokeMiterLimit());
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 7f75609..d9cf8ab 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -124,6 +124,7 @@
     virtual void onPropertyChanged(Properties* properties) = 0;
     virtual ~Node() {}
     virtual void syncProperties() = 0;
+    virtual void setAntiAlias(bool aa) = 0;
 
 protected:
     std::string mName;
@@ -354,6 +355,7 @@
             }
         }
     }
+    virtual void setAntiAlias(bool aa) { mAntiAlias = aa; }
 
 protected:
     const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
@@ -365,6 +367,8 @@
 
     // Intermediate data for drawing, render thread only
     SkPath mTrimmedSkPath;
+    // Default to use AntiAlias
+    bool mAntiAlias = true;
 };
 
 class ANDROID_API ClipPath : public Path {
@@ -373,6 +377,7 @@
     ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
     ClipPath() : Path() {}
     void draw(SkCanvas* outCanvas, bool useStagingData) override;
+    virtual void setAntiAlias(bool aa) {}
 };
 
 class ANDROID_API Group : public Node {
@@ -476,6 +481,12 @@
         }
     }
 
+    virtual void setAntiAlias(bool aa) {
+        for (auto& child : mChildren) {
+            child->setAntiAlias(aa);
+        }
+    }
+
 private:
     GroupProperties mProperties = GroupProperties(this);
     GroupProperties mStagingProperties = GroupProperties(this);
@@ -641,6 +652,8 @@
      */
     void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context);
 
+    void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
+
 private:
     class Cache {
     public:
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b7bb2d15..820789d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -34,6 +34,7 @@
 #include "renderstate/Stencil.h"
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
+#include "../Properties.h"
 
 #include <cutils/properties.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -375,6 +376,9 @@
     }
 
     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+        if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
+            info.out.requiresUiRedraw = true;
+        }
         if (!info.out.requiresUiRedraw) {
             // If animationsNeedsRedraw is set don't bother posting for an RT anim
             // as we will just end up fighting the UI thread.
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index a92cbfd..e7496f7 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,32 +85,8 @@
              outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
              outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
              outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
-             outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767,
-                              22.8911717921705, 16.737515350332117, 24.986664170401575);
-             outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483,
-                              28.644011882390082, 11.155893964798905, 29.37447073281729);
-             outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471,
-                              29.954422532383525, 4.0686829203897235, 28.951642951534332);
-             outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696,
-                              26.161860541657013, -0.9516429515343354, 23.931317079610267);
-             outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987,
-                              19.178055668693663, -1.37447073281729, 16.844106035201087);
-             outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546,
-                              12.523358440585524, 3.0133358295984305, 11.262484649667876);
-             outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984,
-                              9.552224962671648, 10.000000000000005, 10.0);
-             outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881,
-                              12.928932188134523, 2.9289321881345254);
-             outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972,
-                              4.870079381441987E-16, 19.999999999999996, 0.0);
-             outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112,
-                              1.0542876468501678, 27.071067811865476, 2.9289321881345227);
-             outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0,
-                              9.999999999999998);
-             outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112,
-                              27.071067811865476, 17.071067811865476);
-             outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0,
-                              20.000000000000004, 20.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
          }},
 
         // Check box VectorDrawable path data
@@ -181,22 +157,7 @@
          },
          [](SkPath* outPath) {
              outPath->moveTo(300.0, 70.0);
-             outPath->cubicTo(239.06697794203706, 70.13246340443499, 180.6164396449267,
-                              94.47383115953485, 137.6004913602211, 137.6302781499585);
-             outPath->cubicTo(94.58454307551551, 180.78672514038215, 70.43390412842275,
-                              239.3163266242308, 70.50013586976587, 300.2494566687817);
-             outPath->cubicTo(70.56636761110899, 361.1825867133326, 94.84418775550249,
-                              419.65954850554147, 137.9538527586204, 462.72238058830936);
-             outPath->cubicTo(181.06351776173827, 505.7852126710772, 239.5668339599056,
-                              529.999456521097, 300.49999999999994, 529.999456521097);
-             outPath->cubicTo(361.43316604009436, 529.999456521097, 419.93648223826176,
-                              505.78521267107726, 463.0461472413797, 462.7223805883093);
-             outPath->cubicTo(506.1558122444976, 419.65954850554135, 530.433632388891,
-                              361.1825867133324, 530.4998641302341, 300.2494566687815);
-             outPath->cubicTo(530.5660958715771, 239.31632662423056, 506.4154569244844,
-                              180.7867251403819, 463.3995086397787, 137.6302781499583);
-             outPath->cubicTo(420.383560355073, 94.47383115953468, 361.93302205796255,
-                              70.13246340443492, 300.9999999999996, 70.00000000000003);
+             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
              outPath->close();
              outPath->moveTo(300.0, 70.0);
          }},
diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp
index 1931d64..6b8f315 100644
--- a/libs/hwui/utils/VectorDrawableUtils.cpp
+++ b/libs/hwui/utils/VectorDrawableUtils.cpp
@@ -96,132 +96,6 @@
     }
 }
 
-/**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
-static void arcToBezier(SkPath* p, double cx, double cy, double a, double b, double e1x, double e1y,
-                        double theta, double start, double sweep) {
-    // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
-    // and http://www.spaceroots.org/documents/ellipse/node22.html
-
-    // Maximum of 45 degrees per cubic Bezier segment
-    int numSegments = ceil(fabs(sweep * 4 / M_PI));
-
-    double eta1 = start;
-    double cosTheta = cos(theta);
-    double sinTheta = sin(theta);
-    double cosEta1 = cos(eta1);
-    double sinEta1 = sin(eta1);
-    double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
-    double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
-    double anglePerSegment = sweep / numSegments;
-    for (int i = 0; i < numSegments; i++) {
-        double eta2 = eta1 + anglePerSegment;
-        double sinEta2 = sin(eta2);
-        double cosEta2 = cos(eta2);
-        double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
-        double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
-        double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
-        double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
-        double tanDiff2 = tan((eta2 - eta1) / 2);
-        double alpha = sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
-        double q1x = e1x + alpha * ep1x;
-        double q1y = e1y + alpha * ep1y;
-        double q2x = e2x - alpha * ep2x;
-        double q2y = e2y - alpha * ep2y;
-
-        p->cubicTo((float)q1x, (float)q1y, (float)q2x, (float)q2y, (float)e2x, (float)e2y);
-        eta1 = eta2;
-        e1x = e2x;
-        e1y = e2y;
-        ep1x = ep2x;
-        ep1y = ep2y;
-    }
-}
-
-inline double toRadians(float theta) {
-    return theta * M_PI / 180;
-}
-
-static void drawArc(SkPath* p, float x0, float y0, float x1, float y1, float a, float b,
-                    float theta, bool isMoreThanHalf, bool isPositiveArc) {
-    /* Convert rotation angle from degrees to radians */
-    double thetaD = toRadians(theta);
-    /* Pre-compute rotation matrix entries */
-    double cosTheta = cos(thetaD);
-    double sinTheta = sin(thetaD);
-    /* Transform (x0, y0) and (x1, y1) into unit space */
-    /* using (inverse) rotation, followed by (inverse) scale */
-    double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
-    double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
-    double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
-    double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
-    /* Compute differences and averages */
-    double dx = x0p - x1p;
-    double dy = y0p - y1p;
-    double xm = (x0p + x1p) / 2;
-    double ym = (y0p + y1p) / 2;
-    /* Solve for intersecting unit circles */
-    double dsq = dx * dx + dy * dy;
-    if (dsq == 0.0) {
-        VECTOR_DRAWABLE_LOGD("Points are coincident");
-        return; /* Points are coincident */
-    }
-    double disc = 1.0 / dsq - 1.0 / 4.0;
-    if (disc < 0.0) {
-        VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq);
-        float adjust = (float)(sqrt(dsq) / 1.99999);
-        drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta, isMoreThanHalf, isPositiveArc);
-        return; /* Points are too far apart */
-    }
-    double s = sqrt(disc);
-    double sdx = s * dx;
-    double sdy = s * dy;
-    double cx;
-    double cy;
-    if (isMoreThanHalf == isPositiveArc) {
-        cx = xm - sdy;
-        cy = ym + sdx;
-    } else {
-        cx = xm + sdy;
-        cy = ym - sdx;
-    }
-
-    double eta0 = atan2((y0p - cy), (x0p - cx));
-
-    double eta1 = atan2((y1p - cy), (x1p - cx));
-
-    double sweep = (eta1 - eta0);
-    if (isPositiveArc != (sweep >= 0)) {
-        if (sweep > 0) {
-            sweep -= 2 * M_PI;
-        } else {
-            sweep += 2 * M_PI;
-        }
-    }
-
-    cx *= a;
-    cy *= b;
-    double tcx = cx;
-    cx = cx * cosTheta - cy * sinTheta;
-    cy = tcx * sinTheta + cy * cosTheta;
-
-    arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-}
-
 // Use the given verb, and points in the range [start, end) to insert a command into the SkPath.
 void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd,
                               const std::vector<float>* points, size_t start, size_t end) {
@@ -424,18 +298,20 @@
                 break;
             case 'a':  // Draws an elliptical arc
                 // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
-                drawArc(outPath, currentX, currentY, points->at(k + 5) + currentX,
-                        points->at(k + 6) + currentY, points->at(k + 0), points->at(k + 1),
-                        points->at(k + 2), points->at(k + 3) != 0, points->at(k + 4) != 0);
+                outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2),
+                               (SkPath::ArcSize) (points->at(k + 3) != 0),
+                               (SkPath::Direction) (points->at(k + 4) == 0), 
+                               points->at(k + 5) + currentX, points->at(k + 6) + currentY);
                 currentX += points->at(k + 5);
                 currentY += points->at(k + 6);
                 ctrlPointX = currentX;
                 ctrlPointY = currentY;
                 break;
             case 'A':  // Draws an elliptical arc
-                drawArc(outPath, currentX, currentY, points->at(k + 5), points->at(k + 6),
-                        points->at(k + 0), points->at(k + 1), points->at(k + 2),
-                        points->at(k + 3) != 0, points->at(k + 4) != 0);
+                outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2),
+                               (SkPath::ArcSize) (points->at(k + 3) != 0),
+                               (SkPath::Direction) (points->at(k + 4) == 0), 
+                               points->at(k + 5), points->at(k + 6));
                 currentX = points->at(k + 5);
                 currentY = points->at(k + 6);
                 ctrlPointX = currentX;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 968f596..d15ab33 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -184,6 +184,17 @@
     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
 
     /**
+     * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} is
+     * about to be changed through Settings app or Quick Settings.
+     * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
+     * If you're interacting with {@link #isProviderEnabled(String)}, use
+     * {@link #PROVIDERS_CHANGED_ACTION} instead.
+     *
+     * @hide
+     */
+    public static final String MODE_CHANGING_ACTION = "com.android.settings.location.MODE_CHANGING";
+
+    /**
      * Broadcast intent action indicating that the GPS has either started or
      * stopped receiving GPS fixes. An intent extra provides this state as a
      * boolean, where {@code true} means that the GPS is actively receiving fixes.
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 73b2bb5..44d290e 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -7,7 +7,7 @@
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksLocationTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index bb0a944..99499dc 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -56,6 +56,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
 	android.test.runner \
+	android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/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/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index eb33842..3c46d99 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -14,8 +14,10 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.os.BatteryManager;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.print.PrintManager;
 import android.provider.Settings;
@@ -26,6 +28,10 @@
 import java.text.NumberFormat;
 
 public class Utils {
+
+    private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
+    private static final String NEW_MODE_KEY = "NEW_MODE";
+
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
     private static String sServicesSystemSharedLibPackageName;
@@ -39,6 +45,16 @@
         com.android.internal.R.drawable.ic_wifi_signal_4
     };
 
+    public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId) {
+        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+        intent.putExtra(CURRENT_MODE_KEY, oldMode);
+        intent.putExtra(NEW_MODE_KEY, newMode);
+        context.sendBroadcastAsUser(
+                intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        return Settings.Secure.putIntForUser(
+                context.getContentResolver(), Settings.Secure.LOCATION_MODE, newMode, userId);
+    }
+
     /**
      * Return string resource that best describes combination of tethering
      * options available on this device.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index c6cfdb8..976bbee 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -15,20 +15,45 @@
  */
 package com.android.settingslib;
 
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.location.LocationManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSettings;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(
+        manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {UtilsTest.ShadowSecure.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
     private static final String PERCENTAGE_0 = "0%";
@@ -37,6 +62,29 @@
     private static final String PERCENTAGE_50 = "50%";
     private static final String PERCENTAGE_100 = "100%";
 
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        ShadowSecure.reset();
+    }
+
+    @Test
+    public void testUpdateLocationMode_sendBroadcast() {
+        int currentUserId = ActivityManager.getCurrentUser();
+        Utils.updateLocationMode(
+                mContext,
+                Secure.LOCATION_MODE_OFF,
+                Secure.LOCATION_MODE_HIGH_ACCURACY,
+                currentUserId);
+
+        verify(mContext).sendBroadcastAsUser(
+                argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
+                ArgumentMatchers.eq(UserHandle.of(currentUserId)),
+                ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
+    }
+
     @Test
     public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
         final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
@@ -74,4 +122,23 @@
                 .thenReturn(60);
         assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
     }
+
+    private static ArgumentMatcher<Intent> actionMatches(String expected) {
+        return intent -> TextUtils.equals(expected, intent.getAction());
+    }
+
+    @Implements(value = Settings.Secure.class)
+    public static class ShadowSecure extends ShadowSettings.ShadowSecure {
+        private static Map<String, Integer> map = new HashMap<>();
+
+        @Implementation
+        public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+            map.put(name, value);
+            return true;
+        }
+
+        public static void reset() {
+            map.clear();
+        }
+    }
 }
diff --git a/packages/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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ca85445..659c5a8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1989,8 +1989,8 @@
     <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] -->
     <string name="app_info">App info</string>
 
-    <!-- Action label for switching to web for an instant app [CHAR LIMIT=20] -->
-    <string name="go_to_web">Go to web</string>
+    <!-- Action label for switching to a browser for an instant app [CHAR LIMIT=20] -->
+    <string name="go_to_web">Go to browser</string>
 
     <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] -->
     <string name="mobile_data">Mobile data</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 17cb0d8..7db3ac6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -43,6 +43,9 @@
     public void onActivityDismissingDockedStack() { }
     public void onActivityLaunchOnSecondaryDisplayFailed() { }
     public void onTaskProfileLocked(int taskId, int userId) { }
+    public void onTaskRemoved(int taskId) { }
+    public void onTaskMovedToFront(int taskId) { }
+    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
 
     /**
      * Checks that the current user matches the process. Since
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 81c37a9..857e0ea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -145,6 +145,23 @@
         mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
     }
 
+    @Override
+    public void onTaskRemoved(int taskId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_TASK_REMOVED, taskId, 0).sendToTarget();
+    }
+
+    @Override
+    public void onTaskMovedToFront(int taskId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskId, 0).sendToTarget();
+    }
+
+    @Override
+    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
+            throws RemoteException {
+        mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+                requestedOrientation).sendToTarget();
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -157,6 +174,10 @@
         private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
         private static final int ON_ACTIVITY_UNPINNED = 10;
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
+        private static final int ON_TASK_REMOVED = 12;
+        private static final int ON_TASK_MOVED_TO_FRONT = 13;
+        private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 14;
+
 
         public H(Looper looper) {
             super(looper);
@@ -241,6 +262,25 @@
                         }
                         break;
                     }
+                    case ON_TASK_REMOVED: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onTaskRemoved(msg.arg1);
+                        }
+                        break;
+                    }
+                    case ON_TASK_MOVED_TO_FRONT: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onTaskMovedToFront(msg.arg1);
+                        }
+                        break;
+                    }
+                    case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onActivityRequestedOrientationChanged(
+                                    msg.arg1, msg.arg2);
+                        }
+                        break;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/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/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 5ffd785..8d50d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -45,6 +45,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.Utils;
 import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.Dependency;
@@ -431,7 +432,7 @@
     @Override
     public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
         if (picture != null &&
-                UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser()) &&
+                UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
                 !(picture instanceof UserIconDrawable)) {
             picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
             picture.setColorFilter(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 4a91ee0..cdf0c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -63,6 +63,7 @@
     private QSContainerImpl mContainer;
     private int mLayoutDirection;
     private QSFooter mFooter;
+    private float mLastQSExpansion = -1;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -227,6 +228,7 @@
     public void setKeyguardShowing(boolean keyguardShowing) {
         if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
         mKeyguardShowing = keyguardShowing;
+        mLastQSExpansion = -1;
 
         if (mQSAnimator != null) {
             mQSAnimator.setOnKeyguard(keyguardShowing);
@@ -268,6 +270,10 @@
             getView().setTranslationY(mKeyguardShowing ? (translationScaleY * height)
                     : headerTranslation);
         }
+        if (expansion == mLastQSExpansion) {
+            return;
+        }
+        mLastQSExpansion = expansion;
         mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
         mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
         int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index b3d6e32..db19d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -80,10 +80,21 @@
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
         public void getOutline(View view, Outline outline) {
-            Path clipPath = getClipPath();
-            if (clipPath != null && clipPath.isConvex()) {
-                // The path might not be convex in border cases where the view is small and clipped
-                outline.setConvexPath(clipPath);
+            if (!mCustomOutline && mCurrentTopRoundness == 0.0f
+                    && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners) {
+                int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+                int left = Math.max(translation + mCurrentSidePaddings, mCurrentSidePaddings);
+                int top = mClipTopAmount + mBackgroundTop;
+                int right = getWidth() - mCurrentSidePaddings + Math.min(translation, 0);
+                int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
+                outline.setRect(left, top, right, bottom);
+            } else {
+                Path clipPath = getClipPath();
+                if (clipPath != null && clipPath.isConvex()) {
+                    // The path might not be convex in border cases where the view is small and
+                    // clipped
+                    outline.setConvexPath(clipPath);
+                }
             }
             outline.setAlpha(mOutlineAlpha);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/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/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 99debee..c281379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -102,7 +102,7 @@
             return;
         }
 
-        final int activeUserId = ActivityManager.getCurrentUser();
+        final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
         final boolean isSystemUser =
                 UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
         final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/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..c967423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -649,15 +649,6 @@
             .Callback() {
         @Override
         public void onFinished() {
-            notifyKeyguardState();
-        }
-
-        @Override
-        public void onCancelled() {
-            notifyKeyguardState();
-        }
-
-        private void notifyKeyguardState() {
             if (mStatusBarKeyguardViewManager == null) {
                 Log.w(TAG, "Tried to notify keyguard visibility when "
                         + "mStatusBarKeyguardViewManager was null");
@@ -665,6 +656,12 @@
             }
             mStatusBarKeyguardViewManager.onKeyguardFadedAway();
         }
+
+        @Override
+        public void onCancelled() {
+            // Transition was cancelled because another one took over.
+            // Nothing to do in here but wait.
+        }
     };
 
     private NotificationMessagingUtil mMessagingUtil;
@@ -3697,23 +3694,35 @@
      * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
      */
     private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
-        try {
-            if (visibleToUser) {
-                boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
-                boolean clearNotificationEffects =
-                        !isPresenterFullyCollapsed() &&
-                        (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
-                int notificationLoad = mNotificationData.getActiveNotifications().size();
-                if (pinnedHeadsUp && isPresenterFullyCollapsed())  {
-                    notificationLoad = 1;
-                }
-                mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
-            } else {
-                mBarService.onPanelHidden();
+        if (visibleToUser) {
+            boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+            boolean clearNotificationEffects =
+                    !isPresenterFullyCollapsed() &&
+                            (mState == StatusBarState.SHADE
+                                    || mState == StatusBarState.SHADE_LOCKED);
+            int notificationLoad = mNotificationData.getActiveNotifications().size();
+            if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
+                notificationLoad = 1;
             }
-        } catch (RemoteException ex) {
-            // Won't fail unless the world has ended.
+            final int finalNotificationLoad = notificationLoad;
+            mUiOffloadThread.submit(() -> {
+                try {
+                    mBarService.onPanelRevealed(clearNotificationEffects,
+                            finalNotificationLoad);
+                } catch (RemoteException ex) {
+                    // Won't fail unless the world has ended.
+                }
+            });
+        } else {
+            mUiOffloadThread.submit(() -> {
+                try {
+                    mBarService.onPanelHidden();
+                } catch (RemoteException ex) {
+                    // Won't fail unless the world has ended.
+                }
+            });
         }
+
     }
 
     private void stopNotificationLogging() {
@@ -3749,21 +3758,23 @@
                 newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
         NotificationVisibility[] noLongerVisibleAr =
                 noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
-        try {
-            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
-        } catch (RemoteException e) {
-            // Ignore.
-        }
-
-        final int N = newlyVisible.size();
-        if (N > 0) {
-            String[] newlyVisibleKeyAr = new String[N];
-            for (int i = 0; i < N; i++) {
-                newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+        mUiOffloadThread.submit(() -> {
+            try {
+                mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
+            } catch (RemoteException e) {
+                // Ignore.
             }
 
-            setNotificationsShown(newlyVisibleKeyAr);
-        }
+            final int N = newlyVisible.size();
+            if (N > 0) {
+                String[] newlyVisibleKeyAr = new String[N];
+                for (int i = 0; i < N; i++) {
+                    newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+                }
+
+                setNotificationsShown(newlyVisibleKeyAr);
+            }
+        });
     }
 
     // State logging
@@ -5112,6 +5123,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/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 874f0d9..4ee4ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.settingslib.Utils.updateLocationMode;
+
 /**
  * A controller to manage changes of location related states and update the views accordingly.
  */
@@ -106,12 +108,13 @@
         final ContentResolver cr = mContext.getContentResolver();
         // When enabling location, a user consent dialog will pop up, and the
         // setting won't be fully enabled until the user accepts the agreement.
+        int currentMode = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCATION_MODE, 
+                Settings.Secure.LOCATION_MODE_OFF, currentUserId);
         int mode = enabled
                 ? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
         // QuickSettings always runs as the owner, so specifically set the settings
         // for the current foreground user.
-        return Settings.Secure
-                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
+        return updateLocationMode(mContext, currentMode, mode, currentUserId);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 722874b..f258fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -24,6 +24,7 @@
     boolean isRotationLockAffordanceVisible();
     boolean isRotationLocked();
     void setRotationLocked(boolean locked);
+    void setRotationLockedAtAngle(boolean locked, int rotation);
 
     public interface RotationLockControllerCallback {
         void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 4f96496..5418dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -63,6 +63,10 @@
         RotationPolicy.setRotationLock(mContext, locked);
     }
 
+    public void setRotationLockedAtAngle(boolean locked, int rotation){
+        RotationPolicy.setRotationLockAtAngle(mContext, locked, rotation);
+    }
+
     public boolean isRotationLockAffordanceVisible() {
         return RotationPolicy.isRotationLockToggleVisible(mContext);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a3d2423..0f2a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -4443,10 +4443,6 @@
                 mAmbientState.getScrollY()));
     }
 
-    public void setTouchActive(boolean touchActive) {
-        mShelf.setTouchActive(touchActive);
-    }
-
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
index 6b31c96..756bb1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
@@ -62,57 +62,6 @@
     }
 
     @Test
-    public void testParseIntArrayNull() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(null);
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayLeadingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(":4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayEmptyItem() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4::6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayTrailingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6:");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
-    public void testParseIntArrayGoodData() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
     public void testGetOptionsWithNoConfig() throws Exception {
         ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
         assertEquals(3, result.size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 713f7843..5ff90d91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -104,6 +104,7 @@
     PowerManager mPowerManager;
     SystemServicesProxy mSystemServicesProxy;
     NotificationPanelView mNotificationPanelView;
+    ScrimController mScrimController;
     IStatusBarService mBarService;
     ArrayList<Entry> mNotificationList;
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -131,6 +132,7 @@
         mNotificationPanelView = mock(NotificationPanelView.class);
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         mNotificationList = mock(ArrayList.class);
+        mScrimController = mock(ScrimController.class);
         IPowerManager powerManagerService = mock(IPowerManager.class);
         HandlerThread handlerThread = new HandlerThread("TestThread");
         handlerThread.start();
@@ -143,7 +145,7 @@
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
-                mBarService);
+                mBarService, mScrimController);
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         doAnswer(invocation -> {
@@ -428,6 +430,7 @@
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
+            waitForUiOffloadThread();
             verify(mBarService, times(1)).onPanelHidden();
             verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
         } catch (RemoteException e) {
@@ -445,7 +448,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(false, 1);
         } catch (RemoteException e) {
@@ -464,7 +467,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(true, 5);
         } catch (RemoteException e) {
@@ -483,7 +486,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(false, 5);
         } catch (RemoteException e) {
@@ -532,12 +535,21 @@
         mStatusBar.updateKeyguardState(false, false);
     }
 
+    @Test
+    public void testFingerprintNotification_UpdatesScrims() {
+        mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
+        mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class);
+        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
+        mStatusBar.notifyFpAuthModeChanged();
+        verify(mScrimController).transitionTo(any(), any());
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
                 NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
                 PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
-                IStatusBarService barService) {
+                IStatusBarService barService, ScrimController scrimController) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -550,7 +562,7 @@
             mNotificationPanel = panelView;
             mBarService = barService;
             mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
-            mScrimController = mock(ScrimController.class);
+            mScrimController = scrimController;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index d60fe78..be11024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -49,4 +49,9 @@
     public void setRotationLocked(boolean locked) {
 
     }
+
+    @Override
+    public void setRotationLockedAtAngle(boolean locked, int rotation) {
+
+    }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 6faf1f9..ba6bb23 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4138,6 +4138,19 @@
     // OS: O
     FIELD_NOTIFICATION_GROUP_SUMMARY = 947;
 
+    // An app attempted to forge a different component name in the AssisStructure that would be
+    // passed to the autofill service.
+    // OS: O (security patch)
+    // Package: Real package of the app being autofilled
+    // Tag FIELD_AUTOFILL_SERVICE: Package of the autofill service that processed the request
+    // TAG FIELD_AUTOFILL_FORGED_COMPONENT_NAME: Component name being forged
+    AUTOFILL_FORGED_COMPONENT_ATTEMPT = 948;
+
+    // FIELD - The component that an app tried tro forged.
+    // Type: string
+    // OS: O (security patch)
+    FIELD_AUTOFILL_FORGED_COMPONENT_NAME = 949;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // OPEN: Settings > System > Languages & input > Advanced > Lift to open camera
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 ed2ba1f8..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/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..5d4ddf0 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -43,10 +44,12 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
@@ -74,6 +77,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Random;
 
 /**
@@ -463,6 +467,8 @@
             sessionId = sRandom.nextInt();
         } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
 
+        assertCallerLocked(componentName);
+
         final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
                 sessionId, uid, activityToken, appCallbackToken, hasCallback,
                 mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName);
@@ -472,6 +478,34 @@
     }
 
     /**
+     * Asserts the component is owned by the caller.
+     */
+    private void assertCallerLocked(@NonNull ComponentName componentName) {
+        final PackageManager pm = mContext.getPackageManager();
+        final int callingUid = Binder.getCallingUid();
+        final int packageUid;
+        try {
+            packageUid = pm.getPackageUidAsUser(componentName.getPackageName(),
+                    UserHandle.getCallingUserId());
+        } catch (NameNotFoundException e) {
+            throw new SecurityException("Could not verify UID for " + componentName);
+        }
+        if (callingUid != packageUid) {
+            final String[] packages = pm.getPackagesForUid(callingUid);
+            final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
+            Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
+                    + ") passed component (" + componentName + ") owned by UID " + packageUid);
+            mMetricsLogger.write(
+                    Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT,
+                            callingPackage, getServicePackageName())
+                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
+                            componentName == null ? "null" : componentName.flattenToShortString()));
+
+            throw new SecurityException("Invalid component: " + componentName);
+        }
+    }
+
+    /**
      * Restores a session after an activity was temporarily destroyed.
      *
      * @param sessionId The id of the session to restore
@@ -626,7 +660,7 @@
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
-                                null, null, null, null, null, -1));
+                                null, null, null, null, null, null));
             }
         }
     }
@@ -640,7 +674,7 @@
             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                                clientState, null, null, null, null, null, null, null, -1));
+                                clientState, null, null, null, null, null, null, null, null));
             }
         }
     }
@@ -652,7 +686,7 @@
         synchronized (mLock) {
             if (isValidEventLocked("logSaveShown()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null, null, -1));
+                        null, null, null, null, null, null, null));
             }
         }
     }
@@ -666,7 +700,7 @@
             if (isValidEventLocked("logDatasetSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
-                                null, null, null, null, null, null, -1));
+                                null, null, null, null, null, null, null));
             }
         }
     }
@@ -681,14 +715,24 @@
             @Nullable ArrayList<String> changedDatasetIds,
             @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
             @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @Nullable String detectedRemoteId, int detectedFieldScore) {
+            @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 +801,8 @@
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
-        pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
+        pw.print(prefix); pw.print("Field classification enabled: ");
+            pw.println(isFieldClassificationEnabled());
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
@@ -1014,10 +1059,10 @@
         return false;
     }
 
-    // TODO(b/67867469): remove once feature is finished
-    boolean isFieldDetectionEnabled() {
+    boolean isFieldClassificationEnabled() {
         return Settings.Secure.getIntForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+                mContext.getContentResolver(),
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0,
                 mUserId) == 1;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3615bca..4e64afb 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -55,6 +55,7 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -64,6 +65,7 @@
 import android.service.autofill.SaveRequest;
 import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
+import android.service.autofill.EditDistanceScorer;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
@@ -236,10 +238,15 @@
                 structure.ensureData();
 
                 // Sanitize structure before it's sent to service.
-                if (!mComponentName.equals(structure.getActivityComponent())) {
+                final ComponentName componentNameFromApp = structure.getActivityComponent();
+                if (!mComponentName.equals(componentNameFromApp)) {
                     Slog.w(TAG, "Activity " + mComponentName + " forged different component on "
-                            + "AssistStructure: " + structure.getActivityComponent());
+                            + "AssistStructure: " + componentNameFromApp);
                     structure.setActivityComponent(mComponentName);
+                    mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT)
+                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
+                                    componentNameFromApp == null ? "null"
+                                            : componentNameFromApp.flattenToShortString()));
                 }
                 structure.sanitizeForParceling(true);
 
@@ -499,7 +506,7 @@
         }
 
         // TODO(b/67867469): remove once feature is finished
-        if (response.getFieldClassificationIds() != null && !mService.isFieldDetectionEnabled()) {
+        if (response.getFieldClassificationIds() != null && !mService.isFieldClassificationEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
             return;
@@ -942,19 +949,18 @@
         }
 
         final UserData userData = mService.getUserData();
-        final AutofillId detectableFieldId;
-        final String detectableRemoteId;
-        String detectedRemoteId = null;
-        if (userData == null) {
-            detectableFieldId = null;
-            detectableRemoteId = null;
-        } else {
-            // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-            detectableFieldId = fieldClassificationIds[0];
-            detectableRemoteId = userData.getRemoteIds()[0];
-        }
 
-        int detectedFieldScore = -1;
+        final ArrayList<AutofillId> detectedFieldIds;
+        final ArrayList<Match> detectedMatches;
+
+        if (userData != null) {
+            final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
+            detectedFieldIds = new ArrayList<>(maxFieldsSize);
+            detectedMatches = new ArrayList<>(maxFieldsSize);
+        } else {
+            detectedFieldIds = null;
+            detectedMatches = null;
+        }
 
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
@@ -998,8 +1004,8 @@
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (currentValue == null) {
                         if (sDebug) {
-                            Slog.d(TAG, "logContextCommitted(): skipping view witout current value "
-                                    + "( " + viewState + ")");
+                            Slog.d(TAG, "logContextCommitted(): skipping view without current "
+                                    + "value ( " + viewState + ")");
                         }
                         continue;
                     }
@@ -1060,19 +1066,10 @@
                         } // for j
                     }
 
-                    // Check if detectable field changed.
-                    if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
-                            && currentValue.isText() && currentValue.getTextValue() != null) {
-                        final String actualValue = currentValue.getTextValue().toString();
-                        // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-                        final String expectedValue = userData.getValues()[0];
-                        if (actualValue.equalsIgnoreCase(expectedValue)) {
-                            detectedRemoteId = detectableRemoteId;
-                            detectedFieldScore = 0;
-                        } else if (sVerbose) {
-                            Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
-                        }
-                        // TODO(b/67867469): set score on partial hits
+                    // Sets field classification score for field
+                    if (userData!= null) {
+                        setScore(detectedFieldIds, detectedMatches, userData, viewState.id,
+                                currentValue);
                     }
                 } // else
             } // else
@@ -1085,8 +1082,8 @@
                     + ", changedAutofillIds=" + changedFieldIds
                     + ", changedDatasetIds=" + changedDatasetIds
                     + ", manuallyFilledIds=" + manuallyFilledIds
-                    + ", detectableFieldId=" + detectableFieldId
-                    + ", detectedFieldScore=" + detectedFieldScore
+                    + ", detectedFieldIds=" + detectedFieldIds
+                    + ", detectedMatches=" + detectedMatches
                     );
         }
 
@@ -1109,7 +1106,46 @@
         mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
                 changedFieldIds, changedDatasetIds,
                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                detectedRemoteId, detectedFieldScore);
+                detectedFieldIds, detectedMatches);
+    }
+
+    /**
+     * Adds the top score match to {@code detectedFieldsIds} and {@code detectedMatches} for
+     * {@code fieldId} based on its {@code currentValue} and {@code userData}.
+     */
+    private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
+            @NonNull ArrayList<Match> detectedMatches, @NonNull UserData userData,
+            @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) {
+
+        final String[] userValues = userData.getValues();
+        final String[] remoteIds = userData.getRemoteIds();
+
+        // Sanity check
+        if (userValues == null || remoteIds == null || userValues.length != remoteIds.length) {
+            final int valuesLength = userValues == null ? -1 : userValues.length;
+            final int idsLength = remoteIds == null ? -1 : remoteIds.length;
+            Slog.w(TAG, "setScores(): user data mismatch: values.length = "
+                    + valuesLength + ", ids.length = " + idsLength);
+            return;
+        }
+        String remoteId = null;
+        float topScore = 0;
+        for (int i = 0; i < userValues.length; i++) {
+            final String value = userValues[i];
+            final float score = userData.getScorer().getScore(currentValue, value);
+            if (score > topScore) {
+                topScore = score;
+                remoteId = remoteIds[i];
+            }
+        }
+
+        if (remoteId != null && topScore > 0) {
+            if (sVerbose) Slog.v(TAG, "setScores(): top score for #" + fieldId + " is " + topScore);
+            detectedFieldIds.add(fieldId);
+            detectedMatches.add(new Match(remoteId, topScore));
+        } else if (sVerbose) {
+            Slog.v(TAG, "setScores(): no top score for #" + fieldId + ": " + topScore);
+        }
     }
 
     /**
@@ -1675,7 +1711,7 @@
                 break;
             case ACTION_VIEW_ENTERED:
                 if (sVerbose && virtualBounds != null) {
-                    Slog.w(TAG, "entered on virtual child " + id + ": " + virtualBounds);
+                    Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
                 }
                 requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
 
diff --git a/services/backup/java/com/android/server/backup/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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c8bc163..7b4703a 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;
@@ -10023,7 +10024,7 @@
                     }
                 }
 
-                TaskRecord task = new TaskRecord(this,
+                TaskRecord task = TaskRecord.create(this,
                         mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
                         ainfo, intent, description);
                 if (!mRecentTasks.addToBottom(task)) {
@@ -20227,6 +20228,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;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2405890..edf9813 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5022,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 cd3b21c..7561d0f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -114,7 +114,11 @@
 import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.LaunchActivityItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1393,16 +1397,33 @@
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.icicle);
-                mService.mLifecycleManager.scheduleTransaction(app.thread, r.appToken,
-                        new LaunchActivityItem(new Intent(r.intent),
+
+
+                // Create activity launch transaction.
+                final ClientTransaction clientTransaction = new ClientTransaction(app.thread,
+                        r.appToken);
+                clientTransaction.addCallback(new LaunchActivityItem(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
-                        r.persistentState, results, newIntents, !andResume,
-                        mService.isNextTransitionForward(), profilerInfo));
+                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
+                        profilerInfo));
+
+                // Set desired final state.
+                final ActivityLifecycleItem lifecycleItem;
+                if (andResume) {
+                    lifecycleItem = new ResumeActivityItem(mService.isNextTransitionForward());
+                } else {
+                    lifecycleItem = new PauseActivityItem();
+                }
+                clientTransaction.setLifecycleStateRequest(lifecycleItem);
+
+                // Schedule transaction.
+                mService.mLifecycleManager.scheduleTransaction(clientTransaction);
+
 
                 if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
                     // This may be a heavy-weight process!  Note that the package
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/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index af8ecad..75f3056 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -33,11 +33,8 @@
 import android.opengl.Matrix;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
@@ -51,7 +48,6 @@
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.android.internal.R;
 
@@ -84,32 +80,6 @@
     private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
 
     private final Handler mHandler;
-    private final AtomicBoolean mIgnoreAllColorMatrixChanges = new AtomicBoolean();
-    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
-        @Override
-        public void onVrStateChanged(final boolean enabled) {
-            // Turn off all night mode display stuff while device is in VR mode.
-            mIgnoreAllColorMatrixChanges.set(enabled);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    // Cancel in-progress animations
-                    if (mColorMatrixAnimator != null) {
-                        mColorMatrixAnimator.cancel();
-                    }
-
-                    final DisplayTransformManager dtm =
-                            getLocalService(DisplayTransformManager.class);
-                    if (enabled) {
-                        dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, MATRIX_IDENTITY);
-                    } else if (mController != null && mController.isActivated()) {
-                        setMatrix(mController.getColorTemperature(), mMatrixNight);
-                        dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, mMatrixNight);
-                    }
-                }
-            });
-        }
-    };
 
     private float[] mMatrixNight = new float[16];
 
@@ -136,18 +106,6 @@
 
     @Override
     public void onBootPhase(int phase) {
-        if (phase >= PHASE_SYSTEM_SERVICES_READY) {
-            final IVrManager vrManager = IVrManager.Stub.asInterface(
-                    getBinderService(Context.VR_SERVICE));
-            if (vrManager != null) {
-                try {
-                    vrManager.registerListener(mVrStateCallbacks);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to register VR mode state listener: " + e);
-                }
-            }
-        }
-
         if (phase >= PHASE_BOOT_COMPLETED) {
             mBootCompleted = true;
 
@@ -359,11 +317,6 @@
             mColorMatrixAnimator.cancel();
         }
 
-        // Don't do any color matrix change animations if we are ignoring them anyway.
-        if (mIgnoreAllColorMatrixChanges.get()) {
-            return;
-        }
-
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
         final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 019c7c2..7d64aed4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -39,8 +39,10 @@
 import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -86,6 +88,7 @@
     protected final String TAG = getClass().getSimpleName();
     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
 
     /**
@@ -105,12 +108,15 @@
     private final IPackageManager mPm;
     private final UserManager mUm;
     private final Config mConfig;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     // contains connections to all connected services, including app services
     // and system services
     private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
     // things that will be put into mServices as soon as they're ready
     private final ArrayList<String> mServicesBinding = new ArrayList<>();
+    private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+
     // lists the component names of all enabled (and therefore potentially connected)
     // app services for current profiles.
     private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
@@ -890,6 +896,7 @@
 
         final String servicesBindingTag = name.toString() + "/" + userid;
         if (mServicesBinding.contains(servicesBindingTag)) {
+            Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
             // stop registering this thing already! we're working on it
             return;
         }
@@ -938,6 +945,7 @@
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
+                        mServicesRebinding.remove(servicesBindingTag);
                         mServicesBinding.remove(servicesBindingTag);
                         try {
                             mService = asInterface(binder);
@@ -959,6 +967,27 @@
                     mServicesBinding.remove(servicesBindingTag);
                     Slog.v(TAG, getCaption() + " connection lost: " + name);
                 }
+
+                @Override
+                public void onBindingDied(ComponentName name) {
+                    Slog.w(TAG, getCaption() + " binding died: " + name);
+                    synchronized (mMutex) {
+                        mServicesBinding.remove(servicesBindingTag);
+                        mContext.unbindService(this);
+                        if (!mServicesRebinding.contains(servicesBindingTag)) {
+                            mServicesRebinding.add(servicesBindingTag);
+                            mHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        registerService(name, userid);
+                                    }
+                               }, ON_BINDING_DIED_REBIND_DELAY_MS);
+                        } else {
+                            Slog.v(TAG, getCaption() + " not rebinding as "
+                                    + "a previous rebind attempt was made: " + name);
+                        }
+                    }
+                }
             };
             if (!mContext.bindServiceAsUser(intent,
                 serviceConnection,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b746b2c..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_IMPORTANT_INTERRUPTIONS;
-        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_IMPORTANT_INTERRUPTIONS;
-        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) {
@@ -982,7 +966,7 @@
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
         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 2af401f..be66fe2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -500,7 +500,7 @@
             throws InstallerException {
         if (!checkBeforeRemote()) return false;
         try {
-            return mInstalld.snapshotProfile(appId, packageName, codePath);
+            return mInstalld.createProfileSnapshot(appId, packageName, codePath);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 3884916..f0ce3c9 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -60,8 +60,10 @@
     // to synchronize access during policy load and access attempts.
     private static List<Policy> sPolicies = new ArrayList<>();
 
-    // Required MAC permissions files.
-    private static List<File> sMacPermissions = new ArrayList<>();
+    /** Path to MAC permissions on system image */
+    private static final File[] MAC_PERMISSIONS =
+    { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"),
+      new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") };
 
     // Append privapp to existing seinfo label
     private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -74,11 +76,11 @@
 
     /**
      * Load the mac_permissions.xml file containing all seinfo assignments used to
-     * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and
-     * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively.
-     * odm_mac_permissions.xml on /odm partition is optional. For further guidance on
+     * label apps. The loaded mac_permissions.xml file is determined by the
+     * MAC_PERMISSIONS class variable which is set at class load time which itself
+     * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on
      * the proper structure of a mac_permissions.xml file consult the source code
-     * located at system/sepolicy/private/mac_permissions.xml.
+     * located at system/sepolicy/mac_permissions.xml.
      *
      * @return boolean indicating if policy was correctly loaded. A value of false
      *         typically indicates a structural problem with the xml or incorrectly
@@ -91,42 +93,10 @@
 
         FileReader policyFile = null;
         XmlPullParser parser = Xml.newPullParser();
-
-        synchronized (sMacPermissions) {
-            // Only initialize it once.
-            if (sMacPermissions.isEmpty()) {
-                // Platform mac permissions.
-                sMacPermissions.add(new File(
-                    Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
-
-                // Vendor mac permissions.
-                // The filename has been renamed from nonplat_mac_permissions to
-                // vendor_mac_permissions. Either of them should exist.
-                File vendorMacPermission = new File(
-                    Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
-                if (vendorMacPermission.exists()) {
-                    sMacPermissions.add(vendorMacPermission);
-                } else {
-                    // For backward compatibility.
-                    sMacPermissions.add(new File(Environment.getVendorDirectory(),
-                                                 "/etc/selinux/nonplat_mac_permissions.xml"));
-                }
-
-                // ODM mac permissions (optional).
-                File odmMacPermission = new File(
-                    Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml");
-                if (odmMacPermission.exists()) {
-                    sMacPermissions.add(odmMacPermission);
-                }
-            }
-        }
-
-        final int count = sMacPermissions.size();
-        for (int i = 0; i < count; ++i) {
-            File macPermission = sMacPermissions.get(i);
+        for (int i = 0; i < MAC_PERMISSIONS.length; i++) {
             try {
-                policyFile = new FileReader(macPermission);
-                Slog.d(TAG, "Using policy file " + macPermission);
+                policyFile = new FileReader(MAC_PERMISSIONS[i]);
+                Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]);
 
                 parser.setInput(policyFile);
                 parser.nextTag();
@@ -150,13 +120,13 @@
                 StringBuilder sb = new StringBuilder("Exception @");
                 sb.append(parser.getPositionDescription());
                 sb.append(" while parsing ");
-                sb.append(macPermission);
+                sb.append(MAC_PERMISSIONS[i]);
                 sb.append(":");
                 sb.append(ex);
                 Slog.w(TAG, sb.toString());
                 return false;
             } catch (IOException ioe) {
-                Slog.w(TAG, "Exception parsing " + macPermission, ioe);
+                Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe);
                 return false;
             } finally {
                 IoUtils.closeQuietly(policyFile);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1161351..9a7e72a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -211,6 +211,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -3381,7 +3382,7 @@
     }
 
     boolean keyguardOn() {
-        return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
+        return isKeyguardShowingAndNotOccluded();
     }
 
     private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
@@ -4337,7 +4338,7 @@
     // TODO: Should probably be moved into DisplayFrames.
     public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
@@ -4363,12 +4364,15 @@
 
         if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
                 == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+            Rect frame;
             int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+                frame = displayFrames.mUnrestricted;
                 availRight = displayFrames.mUnrestricted.right;
                 availBottom = displayFrames.mUnrestricted.bottom;
             } else {
+                frame = displayFrames.mRestricted;
                 availRight = displayFrames.mRestricted.right;
                 availBottom = displayFrames.mRestricted.bottom;
             }
@@ -4399,10 +4403,12 @@
                 calculateRelevantTaskInsets(taskBounds, outStableInsets,
                         displayWidth, displayHeight);
             }
+            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(frame));
             return mForceShowSystemBars;
         }
         outContentInsets.setEmpty();
         outStableInsets.setEmpty();
+        outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
         return mForceShowSystemBars;
     }
 
@@ -4521,7 +4527,7 @@
 
             w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
                     df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
-                    df /* stableFrame */, df /* outsetFrame */);
+                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout);
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4583,7 +4589,8 @@
         // Let the status bar determine its size.
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                 vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
+                displayFrames.mDisplayCutout);
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
@@ -4714,7 +4721,7 @@
         // And compute the final frame.
         mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                 mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                mTmpNavigationFrame, mTmpNavigationFrame);
+                mTmpNavigationFrame, mTmpNavigationFrame, displayFrames.mDisplayCutout);
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
@@ -5220,7 +5227,7 @@
                 + " sf=" + sf.toShortString()
                 + " osf=" + (osf == null ? "null" : osf.toShortString()));
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
@@ -6872,13 +6879,6 @@
         return mKeyguardOccluded;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        if (mKeyguardDelegate == null) return false;
-        return mKeyguardDelegate.isInputRestricted();
-    }
-
     @Override
     public void dismissKeyguardLw(IKeyguardDismissCallback callback) {
         if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5f067d4..bcb7212 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IWindowManager;
 import android.view.InputEventReceiver;
@@ -210,10 +211,11 @@
          * @param stableFrame The frame around which stable system decoration is positioned.
          * @param outsetFrame The frame that includes areas that aren't part of the surface but we
          * want to treat them as such.
+         * @param displayCutout the display displayCutout
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
                 Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
-                Rect stableFrame, @Nullable Rect outsetFrame);
+                Rect stableFrame, @Nullable Rect outsetFrame, DisplayCutout displayCutout);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -1144,12 +1146,13 @@
      * @param outStableInsets The areas covered by stable system windows irrespective of their
      *                        current visibility. Expressed as positive insets.
      * @param outOutsets The areas that are not real display, but we would like to treat as such.
+     * @param outDisplayCutout The area that has been cut away from the display.
      * @return Whether to always consume the navigation bar.
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
     default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         return false;
     }
 
@@ -1357,17 +1360,6 @@
     public boolean isKeyguardTrustedLw();
 
     /**
-     * inKeyguardRestrictedKeyInputMode
-     *
-     * if keyguard screen is showing or in restricted key input mode (i.e. in
-     * keyguard password emergency screen). When in such mode, certain keys,
-     * such as the Home key and the right soft keys, don't work.
-     *
-     * @return true if in keyguard restricted input mode.
-     */
-    public boolean inKeyguardRestrictedKeyInputMode();
-
-    /**
      * Ask the policy to dismiss the keyguard, if it is currently shown.
      *
      * @param callback Callback to be informed about the result.
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 8a40b4d..6f005a3 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -339,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);
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 0249713..209ce3f 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,8 +21,10 @@
 import static android.view.Surface.ROTATION_90;
 import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
 import java.io.PrintWriter;
@@ -94,6 +96,14 @@
     /** During layout, the current screen borders along which input method windows are placed. */
     public final Rect mDock = new Rect();
 
+    /** Definition of the cutout */
+    @NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+
+    /**
+     * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
+     */
+    public final Rect mDisplayCutoutSafe = new Rect();
+
     private final Rect mDisplayInfoOverscan = new Rect();
     private final Rect mRotatedDisplayInfoOverscan = new Rect();
     public int mDisplayWidth;
@@ -152,7 +162,9 @@
         mStable.set(mUnrestricted);
         mStableFullscreen.set(mUnrestricted);
         mCurrent.set(mUnrestricted);
-
+        mDisplayCutout = DisplayCutout.NO_CUTOUT;
+        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
+                Integer.MAX_VALUE, Integer.MAX_VALUE);
     }
 
     public int getInputMethodWindowVisibleHeight() {
@@ -182,6 +194,7 @@
         dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
         dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
         dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+        pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
     }
 
     private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ad7c300..63eea10 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -23,9 +23,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -45,13 +44,13 @@
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
 
@@ -192,15 +191,17 @@
             int viewVisibility, Rect outContentInsets, Rect outStableInsets,
             InputChannel outInputChannel) {
         return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
-                outContentInsets, outStableInsets, null /* outOutsets */, outInputChannel);
+                outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */,
+                outInputChannel);
     }
 
     @Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets, InputChannel outInputChannel) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout,
+            InputChannel outInputChannel) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-                outContentInsets, outStableInsets, outOutsets, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
     }
 
     @Override
@@ -214,7 +215,7 @@
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-            outContentInsets, outStableInsets, null /* outOutsets */, null);
+            outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */, null);
     }
 
     @Override
@@ -232,6 +233,7 @@
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+            DisplayCutout.ParcelableWrapper cutout,
             MergedConfiguration mergedConfiguration, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
@@ -239,7 +241,8 @@
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
+                outStableInsets, outsets, outBackdropFrame, cutout,
+                mergedConfiguration, outSurface);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 57f754f..94fbd0e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -57,6 +57,7 @@
 import android.os.SystemClock;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -134,6 +135,7 @@
         window.setSession(session);
         final Surface surface = new Surface();
         final Rect tmpRect = new Rect();
+        final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
@@ -196,7 +198,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
-                    tmpRect, null);
+                    tmpRect, tmpCutout, null);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -212,7 +214,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
                     tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -438,7 +440,8 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
                 Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             if (mergedConfiguration != null && mOuter != null
                     && mOuter.mOrientationOnCreation
                             != mergedConfiguration.getMergedConfiguration().orientation) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2a24968..3ad4df7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -191,6 +191,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -1136,9 +1137,9 @@
     }
 
     public int addWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
+            LayoutParams attrs, int viewVisibility, int displayId,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1473,7 +1474,7 @@
                 taskBounds = null;
             }
             if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
-                    outStableInsets, outOutsets)) {
+                    outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
@@ -1853,11 +1854,12 @@
     }
 
     public int relayoutWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int requestedWidth,
+            LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
-            MergedConfiguration mergedConfiguration, Surface outSurface) {
+            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
+            Surface outSurface) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2142,6 +2144,7 @@
             win.mLastRelayoutContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
             outStableInsets.set(win.mStableInsets);
+            outCutout.set(win.mDisplayCutout);
             outOutsets.set(win.mOutsets);
             outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
             if (localLOGV) Slog.v(
@@ -3053,11 +3056,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedInputMode() {
-        return mPolicy.inKeyguardRestrictedKeyInputMode();
-    }
-
-    @Override
     public boolean isKeyguardLocked() {
         return mPolicy.isKeyguardLocked();
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9eab61c..d23a6c7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -42,7 +42,6 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -145,6 +144,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
@@ -321,6 +321,11 @@
     private final Rect mLastOutsets = new Rect();
     private boolean mOutsetsChanged = false;
 
+    /** Part of the display that has been cut away. See {@link DisplayCutout}. */
+    DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private DisplayCutout mLastDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private boolean mDisplayCutoutChanged;
+
     /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
@@ -773,7 +778,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            Rect outsetFrame) {
+            Rect outsetFrame, DisplayCutout displayCutout) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1010,6 +1015,10 @@
         mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
         mStableFrame.offset(-layoutXDiff, -layoutYDiff);
 
+        // TODO(roosa): Figure out what frame exactly this needs to be calculated with.
+        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -1150,8 +1159,9 @@
         mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
         mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
                 (mLastFrame.height() != mFrame.height());
+        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
-                || mOutsetsChanged || mFrameSizeChanged;
+                || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
     }
 
     /**
@@ -1196,6 +1206,7 @@
                 || winAnimator.mSurfaceResized
                 || mOutsetsChanged
                 || mFrameSizeChanged
+                || mDisplayCutoutChanged
                 || configChanged
                 || dragResizingChanged
                 || !isResizedWhileNotDragResizingReported()
@@ -1215,7 +1226,8 @@
                         + " dragResizingChanged=" + dragResizingChanged
                         + " resizedWhileNotDragResizingReported="
                         + isResizedWhileNotDragResizingReported()
-                        + " reportOrientationChanged=" + mReportOrientationChanged);
+                        + " reportOrientationChanged=" + mReportOrientationChanged
+                        + " displayCutoutChanged=" + mDisplayCutoutChanged);
             }
 
             // If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -2850,6 +2862,7 @@
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
             final int displayId = getDisplayId();
+            final DisplayCutout displayCutout = mDisplayCutout;
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -2859,7 +2872,7 @@
                         try {
                             dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                     stableInsets, outsets, reportDraw, mergedConfiguration,
-                                    reportOrientation, displayId);
+                                    reportOrientation, displayId, displayCutout);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
@@ -2867,7 +2880,8 @@
                 });
             } else {
                 dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId);
+                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
+                        displayCutout);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -2881,6 +2895,7 @@
             mStableInsetsChanged = false;
             mOutsetsChanged = false;
             mFrameSizeChanged = false;
+            mDisplayCutoutChanged = false;
             mResizedWhileNotDragResizingReported = true;
             mWinAnimator.mSurfaceResized = false;
             mReportOrientationChanged = false;
@@ -2923,14 +2938,16 @@
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId)
+            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
+            DisplayCutout displayCutout)
             throws RemoteException {
         final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
                 || reportOrientation;
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
-                mPolicy.isNavBarForcedShownLw(this), displayId);
+                mPolicy.isNavBarForcedShownLw(this), displayId,
+                new DisplayCutout.ParcelableWrapper(displayCutout));
         mDragResizingChangeReported = true;
     }
 
@@ -3247,6 +3264,7 @@
                     pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
                     pw.print(" outsets="); mOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mDisplayCutout);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
@@ -3255,6 +3273,7 @@
                     pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.print(" physical="); mLastOutsets.printShortString(pw);
                     pw.print(" outset="); mLastOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
@@ -4279,6 +4298,7 @@
         mLastVisibleInsets.set(mVisibleInsets);
         mLastStableInsets.set(mStableInsets);
         mLastOutsets.set(mOutsets);
+        mLastDisplayCutout = mDisplayCutout;
     }
 
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
diff --git a/services/core/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 14b9dfb..b979ff4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -151,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;
@@ -5022,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.
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/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/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a70441d..d1e0132 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -20,6 +20,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.WindowManager;
 
@@ -34,6 +35,8 @@
     public final Rect stableFrame = new Rect();
     public Rect outsetFrame = new Rect();
 
+    public DisplayCutout displayCutout;
+
     public WindowManager.LayoutParams attrs;
     public int displayId;
     public boolean isVoiceInteraction;
@@ -56,7 +59,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            @Nullable Rect outsetFrame) {
+            @Nullable Rect outsetFrame, DisplayCutout displayCutout) {
         this.parentFrame.set(parentFrame);
         this.displayFrame.set(displayFrame);
         this.overscanFrame.set(overlayFrame);
@@ -65,6 +68,7 @@
         this.decorFrame.set(decorFrame);
         this.stableFrame.set(stableFrame);
         this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
+        this.displayCutout = displayCutout;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 0a644b60..353aa7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -23,6 +23,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 
@@ -37,7 +38,8 @@
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
+            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout)
             throws RemoteException {
 
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c735341..481c898 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -411,11 +411,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        return false;
-    }
-
-    @Override
     public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) {
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index fdcf57b..337fd50 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -22,12 +22,12 @@
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Debug;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IWindow;
@@ -38,6 +38,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
+
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
@@ -158,7 +160,7 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame,0, 0, 1000, 1000);
         assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
         assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -173,7 +175,7 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 100, 100, 200, 200);
         assertRect(w.mContentInsets, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
@@ -194,7 +196,7 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
@@ -202,14 +204,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertRect(w.mFrame, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertRect(w.mFrame, 0, 0, 300, 300);
@@ -222,14 +224,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -237,7 +239,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -247,16 +249,16 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 600, 600, 900, 900);
     }
 
@@ -277,7 +279,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -289,7 +291,7 @@
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
@@ -306,7 +308,7 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
@@ -338,13 +340,13 @@
 
         final Rect policyCrop = new Rect();
 
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
 
         dcf.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
 
@@ -357,7 +359,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
 
         w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
@@ -394,7 +396,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
-                pf /* stableFrame */, null /* outsetFrame */);
+                pf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -413,12 +415,31 @@
 
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
-                cf /* stableFrame */, null /* outsetFrame */);
+                cf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         assertEquals(cf, w.mFrame);
         assertEquals(cf, w.getContentFrameLw());
         assertRect(w.mContentInsets, 0, 0, 0, 0);
     }
 
+    @Test
+    public void testDisplayCutout() {
+        // Regular fullscreen task and window
+        Task task = new TaskWithBounds(null);
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        final Rect pf = new Rect(0, 0, 1000, 1000);
+        // Create a display cutout of size 50x50, aligned top-center
+        final DisplayCutout cutout = createDisplayCutoutFromRect(500, 0, 550, 50);
+
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout);
+
+        assertEquals(w.mDisplayCutout.getSafeInsetTop(), 50);
+        assertEquals(w.mDisplayCutout.getSafeInsetBottom(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetLeft(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetRight(), 0);
+    }
+
     private WindowStateWithTask createWindow(Task task, int width, int height) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
         attrs.width = width;
@@ -426,4 +447,13 @@
 
         return new WindowStateWithTask(attrs, task);
     }
+
+    private DisplayCutout createDisplayCutoutFromRect(int left, int top, int right, int bottom) {
+        return DisplayCutout.fromBoundingPolygon(Arrays.asList(
+                new Point(left, top),
+                new Point (left, bottom),
+                new Point (right, bottom),
+                new Point (left, bottom)
+        ));
+    }
 }
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
index c7657f6..0848fd5 100644
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -21,7 +21,8 @@
 
 LOCAL_JAVA_LIBRARIES := \
     mockito-target \
-    legacy-android-test
+    legacy-android-test \
+    android.test.runner.stubs
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/services/tests/notification/Android.mk b/services/tests/uiservicestests/Android.mk
similarity index 89%
rename from services/tests/notification/Android.mk
rename to services/tests/uiservicestests/Android.mk
index 597a584..40e7878 100644
--- a/services/tests/notification/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -1,5 +1,5 @@
 #########################################################################
-# Build FrameworksNotificationTests package
+# Build FrameworksUiServicesTests package
 #########################################################################
 
 LOCAL_PATH:= $(call my-dir)
@@ -25,12 +25,12 @@
     platform-test-annotations \
     testables
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
-LOCAL_PACKAGE_NAME := FrameworksNotificationTests
+LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
similarity index 91%
rename from services/tests/notification/AndroidManifest.xml
rename to services/tests/uiservicestests/AndroidManifest.xml
index c20020a..621b457 100644
--- a/services/tests/notification/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.tests.notification">
+        package="com.android.frameworks.tests.uiservices">
 
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
@@ -32,6 +32,6 @@
 
     <instrumentation
         android:name="android.testing.TestableInstrumentation"
-        android:targetPackage="com.android.frameworks.tests.notification"
+        android:targetPackage="com.android.frameworks.tests.uiservices"
         android:label="Notification Tests" />
 </manifest>
diff --git a/services/tests/notification/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
similarity index 82%
rename from services/tests/notification/AndroidTest.xml
rename to services/tests/uiservicestests/AndroidTest.xml
index 448bc3d..d3b9d4a 100644
--- a/services/tests/notification/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -13,16 +13,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs Frameworks Notification Tests.">
+<configuration description="Runs Frameworks UI Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="FrameworksNotificationTests.apk" />
+        <option name="test-file-name" value="FrameworksUiServicesTests.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="FrameworksNotificationTests" />
+    <option name="test-tag" value="FrameworksUiServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.frameworks.tests.notification" />
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
         <option name="runner" value="android.testing.TestableInstrumentation" />
     </test>
 </configuration>
diff --git a/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
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 93%
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 ba5ad81..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,49 +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);
-        if (Looper.myLooper() == null) {
-            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/Call.java b/telecomm/java/android/telecom/Call.java
index 16150ba..2091101 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -416,8 +416,15 @@
          */
         public static final int PROPERTY_SELF_MANAGED = 0x00000100;
 
+        /**
+         * Indicates the call used Assisted Dialing.
+         * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
+         * @hide
+         */
+        public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000200
+        // Next PROPERTY value: 0x00000400
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -577,6 +584,9 @@
             if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
                 builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
             }
+            if(hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
+                builder.append(" PROPERTY_ASSISTED_DIALING_USED");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3c32614..aaef8d3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -399,8 +399,14 @@
      */
     public static final int PROPERTY_IS_RTT = 1 << 8;
 
+    /**
+     * Set by the framework to indicate that a connection is using assisted dialing.
+     * @hide
+     */
+    public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
+
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<9
+    // Next PROPERTY value: 1<<10
     //**********************************************************************************************
 
     /**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 6dcc3da..15355ac 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -590,6 +590,14 @@
             ComponentName.createRelative("com.android.phone", ".EmergencyDialer");
 
     /**
+     * The boolean indicated by this extra controls whether or not a call is eligible to undergo
+     * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
+     * @hide
+     */
+    public static final String EXTRA_USE_ASSISTED_DIALING =
+            "android.telecom.extra.USE_ASSISTED_DIALING";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telephony/java/android/telephony/data/DataCallResponse.aidl b/telephony/java/android/telephony/data/DataCallResponse.aidl
new file mode 100644
index 0000000..e4cfd69
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataCallResponse.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable DataCallResponse;
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
new file mode 100644
index 0000000..8cdad3f
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2009 Qualcomm Innovation Center, Inc.  All Rights Reserved.
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description of the response of a setup data call connection request.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DataCallResponse implements Parcelable {
+    private final int mStatus;
+    private final int mSuggestedRetryTime;
+    private final int mCid;
+    private final int mActive;
+    private final String mType;
+    private final String mIfname;
+    private final List<InterfaceAddress> mAddresses;
+    private final List<InetAddress> mDnses;
+    private final List<InetAddress> mGateways;
+    private final List<String> mPcscfs;
+    private final int mMtu;
+
+    /**
+     * @param status Data call fail cause. 0 indicates no error.
+     * @param suggestedRetryTime The suggested data retry time in milliseconds.
+     * @param cid The unique id of the data connection.
+     * @param active Data connection active status. 0 = inactive, 1 = active/physical link down,
+     *               2 = active/physical link up.
+     * @param type The connection protocol, should be one of the PDP_type values in TS 27.007
+     *             section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     * @param ifname The network interface name.
+     * @param addresses A list of addresses with optional "/" prefix length, e.g.,
+     *                  "192.0.1.3" or "192.0.1.11/16 2001:db8::1/64". Typically 1 IPv4 or 1 IPv6 or
+     *                  one of each. If the prefix length is absent the addresses are assumed to be
+     *                  point to point with IPv4 having a prefix length of 32 and IPv6 128.
+     * @param dnses A list of DNS server addresses, e.g., "192.0.1.3" or
+     *              "192.0.1.11 2001:db8::1". Null if no dns server addresses returned.
+     * @param gateways A list of default gateway addresses, e.g., "192.0.1.3" or
+     *                 "192.0.1.11 2001:db8::1". When null, the addresses represent point to point
+     *                 connections.
+     * @param pcscfs A list of Proxy Call State Control Function address via PCO(Protocol
+     *               Configuration Option) for IMS client.
+     * @param mtu MTU (Maximum transmission unit) received from network Value <= 0 means network has
+     *            either not sent a value or sent an invalid value.
+     */
+    public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
+                            @Nullable String type, @Nullable String ifname,
+                            @Nullable List<InterfaceAddress> addresses,
+                            @Nullable List<InetAddress> dnses,
+                            @Nullable List<InetAddress> gateways,
+                            @Nullable List<String> pcscfs, int mtu) {
+        mStatus = status;
+        mSuggestedRetryTime = suggestedRetryTime;
+        mCid = cid;
+        mActive = active;
+        mType = (type == null) ? "" : type;
+        mIfname = (ifname == null) ? "" : ifname;
+        mAddresses = (addresses == null) ? new ArrayList<>() : addresses;
+        mDnses = (dnses == null) ? new ArrayList<>() : dnses;
+        mGateways = (gateways == null) ? new ArrayList<>() : gateways;
+        mPcscfs = (pcscfs == null) ? new ArrayList<>() : pcscfs;
+        mMtu = mtu;
+    }
+
+    public DataCallResponse(Parcel source) {
+        mStatus = source.readInt();
+        mSuggestedRetryTime = source.readInt();
+        mCid = source.readInt();
+        mActive = source.readInt();
+        mType = source.readString();
+        mIfname = source.readString();
+        mAddresses = new ArrayList<>();
+        source.readList(mAddresses, InterfaceAddress.class.getClassLoader());
+        mDnses = new ArrayList<>();
+        source.readList(mDnses, InetAddress.class.getClassLoader());
+        mGateways = new ArrayList<>();
+        source.readList(mGateways, InetAddress.class.getClassLoader());
+        mPcscfs = new ArrayList<>();
+        source.readList(mPcscfs, InetAddress.class.getClassLoader());
+        mMtu = source.readInt();
+    }
+
+    /**
+     * @return Data call fail cause. 0 indicates no error.
+     */
+    public int getStatus() { return mStatus; }
+
+    /**
+     * @return The suggested data retry time in milliseconds.
+     */
+    public int getSuggestedRetryTime() { return mSuggestedRetryTime; }
+
+
+    /**
+     * @return The unique id of the data connection.
+     */
+    public int getCallId() { return mCid; }
+
+    /**
+     * @return 0 = inactive, 1 = active/physical link down, 2 = active/physical link up.
+     */
+    public int getActive() { return mActive; }
+
+    /**
+     * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section
+     * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     */
+    @NonNull
+    public String getType() { return mType; }
+
+    /**
+     * @return The network interface name.
+     */
+    @NonNull
+    public String getIfname() { return mIfname; }
+
+    /**
+     * @return A list of {@link InterfaceAddress}
+     */
+    @NonNull
+    public List<InterfaceAddress> getAddresses() { return mAddresses; }
+
+    /**
+     * @return A list of DNS server addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if no dns server addresses returned.
+     */
+    @NonNull
+    public List<InetAddress> getDnses() { return mDnses; }
+
+    /**
+     * @return A list of default gateway addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if the addresses represent point to point connections.
+     */
+    @NonNull
+    public List<InetAddress> getGateways() { return mGateways; }
+
+    /**
+     * @return A list of Proxy Call State Control Function address via PCO(Protocol Configuration
+     * Option) for IMS client.
+     */
+    @NonNull
+    public List<String> getPcscfs() { return mPcscfs; }
+
+    /**
+     * @return MTU received from network Value <= 0 means network has either not sent a value or
+     * sent an invalid value
+     */
+    public int getMtu() { return mMtu; }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("DataCallResponse: {")
+           .append(" status=").append(mStatus)
+           .append(" retry=").append(mSuggestedRetryTime)
+           .append(" cid=").append(mCid)
+           .append(" active=").append(mActive)
+           .append(" type=").append(mType)
+           .append(" ifname=").append(mIfname)
+           .append(" mtu=").append(mMtu)
+           .append(" addresses=").append(mAddresses)
+           .append(" dnses=").append(mDnses)
+           .append(" gateways=").append(mGateways)
+           .append(" pcscf=").append(mPcscfs)
+           .append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mStatus);
+        dest.writeInt(mSuggestedRetryTime);
+        dest.writeInt(mCid);
+        dest.writeInt(mActive);
+        dest.writeString(mType);
+        dest.writeString(mIfname);
+        dest.writeList(mAddresses);
+        dest.writeList(mDnses);
+        dest.writeList(mGateways);
+        dest.writeList(mPcscfs);
+        dest.writeInt(mMtu);
+    }
+
+    public static final Parcelable.Creator<DataCallResponse> CREATOR =
+            new Parcelable.Creator<DataCallResponse>() {
+                @Override
+                public DataCallResponse createFromParcel(Parcel source) {
+                    return new DataCallResponse(source);
+                }
+
+                @Override
+                public DataCallResponse[] newArray(int size) {
+                    return new DataCallResponse[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/data/InterfaceAddress.aidl
new file mode 100644
index 0000000..d750363
--- /dev/null
+++ b/telephony/java/android/telephony/data/InterfaceAddress.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable InterfaceAddress;
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java
new file mode 100644
index 0000000..947d0ff
--- /dev/null
+++ b/telephony/java/android/telephony/data/InterfaceAddress.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.SystemApi;
+import android.net.NetworkUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a Network Interface address. In short it's an IP address, a subnet mask
+ * when the address is an IPv4 one. An IP address and a network prefix length in the case of IPv6
+ * address.
+ *
+ * @hide
+ */
+@SystemApi
+public final class InterfaceAddress implements Parcelable {
+
+    private final InetAddress mInetAddress;
+
+    private final int mPrefixLength;
+
+    /**
+     * @param inetAddress A {@link InetAddress} of the address
+     * @param prefixLength The network prefix length for this address.
+     */
+    public InterfaceAddress(InetAddress inetAddress, int prefixLength) {
+        mInetAddress = inetAddress;
+        mPrefixLength = prefixLength;
+    }
+
+    /**
+     * @param address The address in string format
+     * @param prefixLength The network prefix length for this address.
+     * @throws UnknownHostException
+     */
+    public InterfaceAddress(String address, int prefixLength) throws UnknownHostException {
+        InetAddress ia;
+        try {
+            ia = NetworkUtils.numericToInetAddress(address);
+        } catch (IllegalArgumentException e) {
+            throw new UnknownHostException("Non-numeric ip addr=" + address);
+        }
+        mInetAddress = ia;
+        mPrefixLength = prefixLength;
+    }
+
+    public InterfaceAddress(Parcel source) {
+        mInetAddress = (InetAddress) source.readSerializable();
+        mPrefixLength = source.readInt();
+    }
+
+    /**
+     * @return an InetAddress for this address.
+     */
+    public InetAddress getAddress() { return mInetAddress; }
+
+    /**
+     * @return The network prefix length for this address.
+     */
+    public int getNetworkPrefixLength() { return mPrefixLength; }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return mInetAddress + "/" + mPrefixLength;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeSerializable(mInetAddress);
+        dest.writeInt(mPrefixLength);
+    }
+
+    public static final Parcelable.Creator<InterfaceAddress> CREATOR =
+            new Parcelable.Creator<InterfaceAddress>() {
+        @Override
+        public InterfaceAddress createFromParcel(Parcel source) {
+            return new InterfaceAddress(source);
+        }
+
+        @Override
+        public InterfaceAddress[] newArray(int size) {
+            return new InterfaceAddress[size];
+        }
+    };
+}
diff --git a/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/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index 6b9bb31..9174014 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.View;
@@ -103,7 +104,8 @@
                         WindowManagerGlobal.getWindowSession().relayout(window,
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
-                                new MergedConfiguration(), new Surface());
+                                new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
+                                new Surface());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
diff --git a/tests/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/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/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index 7d74a72..b4e3097 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import android.annotation.NonNull;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.AttachCallback;
 import android.net.wifi.aware.DiscoverySessionCallback;
@@ -168,7 +169,7 @@
          * @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder addWifiAwarePeer(@NonNull byte[] peerMacAddress) {
+        public Builder addWifiAwarePeer(@NonNull MacAddress peerMacAddress) {
             if (peerMacAddress == null) {
                 throw new IllegalArgumentException("Null peer MAC address");
             }
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index 93e52ae..a380fae 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -17,16 +17,15 @@
 package android.net.wifi.rtt;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.MacAddress;
 import android.net.wifi.aware.PeerHandle;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import libcore.util.HexEncoding;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -62,7 +61,7 @@
     public static final int STATUS_FAIL = 1;
 
     private final int mStatus;
-    private final byte[] mMac;
+    private final MacAddress mMac;
     private final PeerHandle mPeerHandle;
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
@@ -70,7 +69,7 @@
     private final long mTimestamp;
 
     /** @hide */
-    public RangingResult(@RangeResultStatus int status, byte[] mac, int distanceMm,
+    public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
             int distanceStdDevMm, int rssi, long timestamp) {
         mStatus = status;
         mMac = mac;
@@ -109,7 +108,7 @@
      * Will return a {@code null} for results corresponding to requests issued using a {@code
      * PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API.
      */
-    public byte[] getMacAddress() {
+    public MacAddress getMacAddress() {
         return mMac;
     }
 
@@ -193,7 +192,12 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mStatus);
-        dest.writeByteArray(mMac);
+        if (mMac == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mMac.writeToParcel(dest, flags);
+        }
         if (mPeerHandle == null) {
             dest.writeBoolean(false);
         } else {
@@ -216,7 +220,11 @@
         @Override
         public RangingResult createFromParcel(Parcel in) {
             int status = in.readInt();
-            byte[] mac = in.createByteArray();
+            boolean macAddressPresent = in.readBoolean();
+            MacAddress mac = null;
+            if (macAddressPresent) {
+                mac = MacAddress.CREATOR.createFromParcel(in);
+            }
             boolean peerHandlePresent = in.readBoolean();
             PeerHandle peerHandle = null;
             if (peerHandlePresent) {
@@ -240,11 +248,11 @@
     @Override
     public String toString() {
         return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
-                mMac == null ? "<null>" : new String(HexEncoding.encodeToString(mMac))).append(
-                ", peerHandle=").append(mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(
-                ", distanceMm=").append(mDistanceMm).append(", distanceStdDevMm=").append(
-                mDistanceStdDevMm).append(", rssi=").append(mRssi).append(", timestamp=").append(
-                mTimestamp).append("]").toString();
+                mMac).append(", peerHandle=").append(
+                mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
+                mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
+                ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
+                "]").toString();
     }
 
     @Override
@@ -259,7 +267,7 @@
 
         RangingResult lhs = (RangingResult) o;
 
-        return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac) && Objects.equals(
+        return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
                 && mTimestamp == lhs.mTimestamp;
diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
index 1090bfa..8be7782 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
@@ -24,11 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import libcore.util.HexEncoding;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -127,13 +124,13 @@
      * peerHandle field) ise used to identify the Responder.
      * TODO: convert to MacAddress
      */
-    public byte[] macAddress;
+    public MacAddress macAddress;
 
     /**
      * The peer identifier of a Wi-Fi Aware Responder. Will be null if a MAC Address (the macAddress
      * field) is used to identify the Responder.
      */
-    public final PeerHandle peerHandle;
+    public PeerHandle peerHandle;
 
     /**
      * The device type of the Responder.
@@ -194,9 +191,13 @@
      * @param preamble        The preamble used by the Responder, specified using
      *                        {@link PreambleType}.
      */
-    public ResponderConfig(@NonNull byte[] macAddress, @ResponderType int responderType,
+    public ResponderConfig(@NonNull MacAddress macAddress, @ResponderType int responderType,
             boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
             int centerFreq1, @PreambleType int preamble) {
+        if (macAddress == null) {
+            throw new IllegalArgumentException(
+                    "Invalid ResponderConfig - must specify a MAC address");
+        }
         this.macAddress = macAddress;
         this.peerHandle = null;
         this.responderType = responderType;
@@ -248,10 +249,7 @@
      * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}.
      */
     public static ResponderConfig fromScanResult(ScanResult scanResult) {
-        byte[] macAddress = null;
-        if (scanResult.BSSID != null) {
-            macAddress = MacAddress.byteAddrFromStringAddr(scanResult.BSSID);
-        }
+        MacAddress macAddress = MacAddress.fromString(scanResult.BSSID);
         int responderType = RESPONDER_AP;
         boolean supports80211mc = scanResult.is80211mcResponder();
         int channelWidth = translcateScanResultChannelWidth(scanResult.channelWidth);
@@ -275,7 +273,7 @@
      * Creates a Responder configuration from a MAC address corresponding to a Wi-Fi Aware
      * Responder. The Responder parameters are set to defaults.
      */
-    public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(byte[] macAddress) {
+    public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(MacAddress macAddress) {
         /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
          * is expected to be brought up and available to negotiate a maximum accuracy channel
          * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
@@ -323,11 +321,16 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByteArray(macAddress);
-        if (peerHandle == null) {
-            dest.writeInt(0);
+        if (macAddress == null) {
+            dest.writeBoolean(false);
         } else {
-            dest.writeInt(1);
+            dest.writeBoolean(true);
+            macAddress.writeToParcel(dest, flags);
+        }
+        if (peerHandle == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
             dest.writeInt(peerHandle.peerId);
         }
         dest.writeInt(responderType);
@@ -347,10 +350,14 @@
 
         @Override
         public ResponderConfig createFromParcel(Parcel in) {
-            byte[] macAddress = in.createByteArray();
-            int peerHandleFlag = in.readInt();
+            boolean macAddressPresent = in.readBoolean();
+            MacAddress macAddress = null;
+            if (macAddressPresent) {
+                macAddress = MacAddress.CREATOR.createFromParcel(in);
+            }
+            boolean peerHandlePresent = in.readBoolean();
             PeerHandle peerHandle = null;
-            if (peerHandleFlag == 1) {
+            if (peerHandlePresent) {
                 peerHandle = new PeerHandle(in.readInt());
             }
             int responderType = in.readInt();
@@ -383,7 +390,7 @@
 
         ResponderConfig lhs = (ResponderConfig) o;
 
-        return Arrays.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
+        return Objects.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
                 lhs.peerHandle) && responderType == lhs.responderType
                 && supports80211mc == lhs.supports80211mc && channelWidth == lhs.channelWidth
                 && frequency == lhs.frequency && centerFreq0 == lhs.centerFreq0
@@ -399,8 +406,7 @@
     /** @hide */
     @Override
     public String toString() {
-        return new StringBuffer("ResponderConfig: macAddress=").append(
-                macAddress == null ? "<null>" : new String(HexEncoding.encode(macAddress))).append(
+        return new StringBuffer("ResponderConfig: macAddress=").append(macAddress).append(
                 ", peerHandle=").append(peerHandle == null ? "<null>" : peerHandle.peerId).append(
                 ", responderType=").append(responderType).append(", supports80211mc=").append(
                 supports80211mc).append(", channelWidth=").append(channelWidth).append(
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index c98e40a..d9f332f 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -58,6 +58,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
+    android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 300d425..72e95b9 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.PeerHandle;
 import android.os.Handler;
@@ -33,8 +34,6 @@
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import libcore.util.HexEncoding;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -73,13 +72,15 @@
     }
 
     /**
-     * Validate ranging call flow with succesful results.
+     * Validate ranging call flow with successful results.
      */
     @Test
     public void testRangeSuccess() throws Exception {
         RangingRequest request = new RangingRequest.Builder().build();
         List<RangingResult> results = new ArrayList<>();
-        results.add(new RangingResult(RangingResult.STATUS_SUCCESS, (byte[]) null, 15, 5, 10, 666));
+        results.add(
+                new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
+                        10, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -135,7 +136,7 @@
         List<ScanResult> scanResults2and3 = new ArrayList<>(2);
         scanResults2and3.add(scanResult2);
         scanResults2and3.add(scanResult3);
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
         PeerHandle peerHandle1 = new PeerHandle(12);
 
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -169,7 +170,7 @@
         for (int i = 0; i < RangingRequest.getMaxPeers() - 3; ++i) {
             scanResultList.add(scanResult);
         }
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
 
         // create request
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -189,11 +190,12 @@
     @Test(expected = IllegalArgumentException.class)
     public void testRangingRequestPastLimit() {
         ScanResult scanResult = new ScanResult();
+        scanResult.BSSID = "00:01:02:03:04:05";
         List<ScanResult> scanResultList = new ArrayList<>();
         for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
             scanResultList.add(scanResult);
         }
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
 
         // create request
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -228,7 +230,7 @@
     public void testRangingResultsParcel() {
         // Note: not validating parcel code of ScanResult (assumed to work)
         int status = RangingResult.STATUS_SUCCESS;
-        final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
+        final MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
         PeerHandle peerHandle = new PeerHandle(10);
         int distanceCm = 105;
         int distanceStdDevCm = 10;