Merge "Import some plugin documentation into the tree"
diff --git a/api/current.txt b/api/current.txt
index c0223b7..1c97c66 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6595,7 +6595,7 @@
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
     method public boolean updateOverrideApn(android.content.ComponentName, int, android.telephony.data.ApnSetting);
     method public void wipeData(int);
-    method public void wipeDataWithReason(int, java.lang.CharSequence);
+    method public void wipeData(int, java.lang.CharSequence);
     field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
     field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
@@ -14510,7 +14510,9 @@
     ctor public AnimatedImageDrawable();
     method public void clearAnimationCallbacks();
     method public void draw(android.graphics.Canvas);
+    method public int getLoopCount();
     method public int getOpacity();
+    method public final boolean isAutoMirrored();
     method public boolean isRunning();
     method public void registerAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback);
     method public void setAlpha(int);
@@ -24580,13 +24582,19 @@
     field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
     field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
     field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0
+    field public static final int GROUP_UNKNOWN = -1; // 0xffffffff
+    field public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1; // 0xffffffff
     field public static final int LOCATION_MAINBODY = 1; // 0x1
     field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
     field public static final int LOCATION_PERIPHERAL = 3; // 0x3
     field public static final int LOCATION_UNKNOWN = 0; // 0x0
+    field public static final android.media.MicrophoneInfo.Coordinate3F ORIENTATION_UNKNOWN;
+    field public static final android.media.MicrophoneInfo.Coordinate3F POSITION_UNKNOWN;
+    field public static final float SENSITIVITY_UNKNOWN = -3.4028235E38f;
+    field public static final float SPL_UNKNOWN = -3.4028235E38f;
   }
 
-  public class MicrophoneInfo.Coordinate3F {
+  public static final class MicrophoneInfo.Coordinate3F {
     field public final float x;
     field public final float y;
     field public final float z;
@@ -32987,7 +32995,7 @@
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
-    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
+    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnThreadViolationListener);
     method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
     method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
     method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -33019,7 +33027,7 @@
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
-    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
+    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnVmViolationListener);
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
   }
@@ -38253,17 +38261,6 @@
     method public byte[] transmit(byte[]) throws java.io.IOException;
   }
 
-  public abstract interface ISecureElementListener implements android.os.IInterface {
-    method public abstract void serviceConnected() throws android.os.RemoteException;
-  }
-
-  public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
-    ctor public ISecureElementListener.Stub();
-    method public android.os.IBinder asBinder();
-    method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
-    method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
-  }
-
   public class Reader {
     method public void closeSessions();
     method public java.lang.String getName();
@@ -38273,13 +38270,19 @@
   }
 
   public class SEService {
-    ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
+    ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
     method public android.se.omapi.Reader[] getReaders();
     method public java.lang.String getVersion();
     method public boolean isConnected();
     method public void shutdown();
   }
 
+  public static abstract class SEService.SecureElementListener extends android.os.Binder {
+    ctor public SEService.SecureElementListener();
+    method public android.os.IBinder asBinder();
+    method public void serviceConnected();
+  }
+
   public class Session {
     method public void close();
     method public void closeChannels();
diff --git a/api/removed.txt b/api/removed.txt
index 55022f3..79c54fd 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -257,6 +257,14 @@
     ctor public RecoverySystem();
   }
 
+  public static final class StrictMode.ThreadPolicy.Builder {
+    method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
+  }
+
+  public static final class StrictMode.VmPolicy.Builder {
+    method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
+  }
+
   public final class SystemClock {
     method public static java.time.Clock elapsedRealtimeClock();
     method public static java.time.Clock uptimeClock();
diff --git a/api/system-current.txt b/api/system-current.txt
index d24a313..0932223 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -204,6 +204,7 @@
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+    field public static final int userRestriction = 16844165; // 0x1010585
   }
 
   public static final class R.raw {
@@ -4176,6 +4177,8 @@
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
     field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
+    field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
+    field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
@@ -4271,7 +4274,7 @@
     method public int getMaxAttempts();
     method public byte[] getServerParams();
     method public int getSnapshotVersion();
-    method public byte[] getTrustedHardwarePublicKey();
+    method public java.security.cert.CertPath getTrustedHardwareCertPath();
     method public java.util.List<android.security.keystore.recovery.WrappedApplicationKey> getWrappedApplicationKeys();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainSnapshot> CREATOR;
@@ -4294,12 +4297,11 @@
   public class RecoveryController {
     method public android.security.keystore.recovery.RecoverySession createRecoverySession();
     method public byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
-    method public java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
     method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -4315,7 +4317,7 @@
   public class RecoverySession implements java.lang.AutoCloseable {
     method public void close();
     method public java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
-    method public byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
   }
 
   public class SessionExpiredException extends java.security.GeneralSecurityException {
@@ -5220,6 +5222,7 @@
     method public int describeContents();
     method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo);
     method public int getCarrierPrivilegeStatus(android.content.pm.Signature, java.lang.String);
+    method public java.lang.String getCertificateHexString();
     method public java.lang.String getPackageName();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
diff --git a/api/system-removed.txt b/api/system-removed.txt
index ac40052..cd56c46 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -93,10 +93,21 @@
 
 package android.security.keystore.recovery {
 
+  public final class KeyChainSnapshot implements android.os.Parcelable {
+    method public deprecated byte[] getTrustedHardwarePublicKey();
+  }
+
   public class RecoveryController {
+    method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
   }
 
+  public class RecoverySession implements java.lang.AutoCloseable {
+    method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+  }
+
 }
 
 package android.service.notification {
diff --git a/api/test-current.txt b/api/test-current.txt
index 55e7926..d5b4311 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -732,6 +732,10 @@
     field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
   }
 
+  public class ServiceState implements android.os.Parcelable {
+    method public void setSystemAndNetworkId(int, int);
+  }
+
 }
 
 package android.telephony.mbms {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 244fbce..87825f1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -249,7 +249,8 @@
                    benchmark/main.cpp \
                    benchmark/hello_world_benchmark.cpp \
                    benchmark/log_event_benchmark.cpp \
-                   benchmark/stats_write_benchmark.cpp
+                   benchmark/stats_write_benchmark.cpp \
+                   benchmark/filter_value_benchmark.cpp
 
 LOCAL_C_INCLUDES := $(statsd_common_c_includes)
 
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
new file mode 100644
index 0000000..b9ddf36
--- /dev/null
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vector>
+#include "benchmark/benchmark.h"
+#include "FieldValue.h"
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::vector;
+
+static void BM_FilterValue(benchmark::State& state) {
+    LogEvent event(1, 100000);
+    event.write(3.2f);
+    event.write("LOCATION");
+    event.write((int64_t)990);
+    event.init();
+
+    FieldMatcher field_matcher;
+    field_matcher.set_field(1);
+    field_matcher.add_child()->set_field(2);
+    field_matcher.add_child()->set_field(3);
+
+    std::vector<Matcher> matchers;
+    translateFieldMatcher(field_matcher, &matchers);
+
+    while (state.KeepRunning()) {
+        vector<HashableDimensionKey> output;
+        filterValues(matchers, event.getValues(), &output);
+    }
+}
+
+BENCHMARK(BM_FilterValue);
+
+}  //  namespace statsd
+}  //  namespace os
+}  //  namespace android
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 6894bcf..b541612 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -135,6 +135,8 @@
         case STRING:
             str_value = from.str_value;
             break;
+        default:
+            break;
     }
 }
 
@@ -148,6 +150,8 @@
             return std::to_string(float_value) + "[F]";
         case STRING:
             return str_value + "[S]";
+        default:
+            return "[UNKNOWN]";
     }
 }
 
@@ -163,6 +167,8 @@
             return float_value == that.float_value;
         case STRING:
             return str_value == that.str_value;
+        default:
+            return false;
     }
 }
 
@@ -177,6 +183,8 @@
             return float_value != that.float_value;
         case STRING:
             return str_value != that.str_value;
+        default:
+            return false;
     }
 }
 
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index d17dded..21f30e2 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -31,7 +31,7 @@
 const int32_t kLastBitMask = 0x80;
 const int32_t kClearLastBitDeco = 0x7f;
 
-enum Type { INT, LONG, FLOAT, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, STRING };
 
 int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
 
@@ -82,6 +82,8 @@
     int32_t mField;
 
 public:
+    Field() {}
+
     Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
         mField = getEncodedField(pos, depth, true);
     }
@@ -229,6 +231,8 @@
  *
  */
 struct Value {
+    Value() : type(UNKNOWN) {}
+
     Value(int32_t v) {
         int_value = v;
         type = INT;
@@ -280,15 +284,13 @@
     bool operator!=(const Value& that) const;
 
     bool operator<(const Value& that) const;
-
-private:
-    Value(){};
 };
 
 /**
  * Represents a log item, or a dimension item (They are essentially the same).
  */
 struct FieldValue {
+    FieldValue() {}
     FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
     }
     bool operator==(const FieldValue& that) const {
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 3c69f9e..1502a00 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -16,6 +16,8 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
+#include <mutex>
+
 #include "HashableDimensionKey.h"
 #include "FieldValue.h"
 
@@ -46,10 +48,12 @@
                                                              fieldValue.mValue.str_value)));
                 break;
             case FLOAT: {
-                float floatVal = fieldValue.mValue.float_value;
-                hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+                hash = android::JenkinsHashMix(hash,
+                                               android::hash_type(fieldValue.mValue.float_value));
                 break;
             }
+            default:
+                break;
         }
     }
     return JenkinsHashWhiten(hash);
@@ -64,26 +68,32 @@
     int prevAnyMatcherPrefix = 0;
     size_t prevPrevFanout = 0;
     size_t prevFanout = 0;
+
     // For each matcher get matched results.
+    vector<FieldValue> matchedResults(2);
     for (const auto& matcher : matcherFields) {
-        vector<FieldValue> matchedResults;
+        size_t num_matches = 0;
         for (const auto& value : values) {
             // TODO: potential optimization here to break early because all fields are naturally
             // sorted.
             if (value.mField.matches(matcher)) {
-                matchedResults.push_back(FieldValue(
-                        Field(value.mField.getTag(), (value.mField.getField() & matcher.mMask)),
-                        value.mValue));
+                if (num_matches >= matchedResults.size()) {
+                    matchedResults.resize(num_matches * 2);
+                }
+                matchedResults[num_matches].mField.setTag(value.mField.getTag());
+                matchedResults[num_matches].mField.setField(value.mField.getField() & matcher.mMask);
+                matchedResults[num_matches].mValue = value.mValue;
+                num_matches++;
             }
         }
 
-        if (matchedResults.size() == 0) {
+        if (num_matches == 0) {
             VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
                    matcher.mMatcher.getField());
             continue;
         }
 
-        if (matchedResults.size() == 1) {
+        if (num_matches == 1) {
             for (auto& dimension : *output) {
                 dimension.addValue(matchedResults[0]);
             }
@@ -119,23 +129,23 @@
             // First create fanout (fanout size is matchedResults.Size which could be one,
             // which means we do nothing here)
             oldSize = output->size();
-            for (size_t i = 1; i < matchedResults.size(); i++) {
+            for (size_t i = 1; i < num_matches; i++) {
                 output->insert(output->end(), output->begin(), output->begin() + oldSize);
             }
             prevPrevFanout = oldSize;
-            prevFanout = matchedResults.size();
+            prevFanout = num_matches;
         } else {
             // If we should not create fanout, e.g., uid tag from same position should be remain
             // together.
             oldSize = prevPrevFanout;
-            if (prevFanout != matchedResults.size()) {
+            if (prevFanout != num_matches) {
                 // sanity check.
                 ALOGE("2 Any matcher result in different output");
                 return false;
             }
         }
         // now add the matched field value to output
-        for (size_t i = 0; i < matchedResults.size(); i++) {
+        for (size_t i = 0; i < num_matches; i++) {
             for (int j = 0; j < oldSize; j++) {
                 (*output)[i * oldSize + j].addValue(matchedResults[i]);
             }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2b02025..04ebfcd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -940,6 +940,11 @@
     // Empty if not set.
     optional string launch_token = 13;
 
+    // The compiler filter used when when the package was optimized.
+    optional string package_optimization_compilation_filter = 14;
+
+    // The reason why the package was optimized.
+    optional string package_optimization_compilation_reason = 15;
 }
 
 message AppStartCancelChanged {
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 2c7b919..78ebe33 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -183,6 +183,8 @@
                 case STRING:
                     protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
                     break;
+                default:
+                    break;
             }
             (*index)++;
         } else if (valueDepth > depth && valuePrefix == prefix) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3ebe89f..3d04e2c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4161,9 +4161,10 @@
      * <p>You can override this function to force global search, e.g. in response to a dedicated
      * search key, or to block search entirely (by simply returning false).
      *
-     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION}, the default
-     * implementation changes to simply return false and you must supply your own custom
-     * implementation if you want to support search.</p>
+     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION} or
+     * {@link Configuration#UI_MODE_TYPE_WATCH}, the default implementation changes to simply
+     * return false and you must supply your own custom implementation if you want to support
+     * search.
      *
      * @param searchEvent The {@link SearchEvent} that signaled this search.
      * @return Returns {@code true} if search launched, and {@code false} if the activity does
@@ -4183,8 +4184,10 @@
      * @see #onSearchRequested(SearchEvent)
      */
     public boolean onSearchRequested() {
-        if ((getResources().getConfiguration().uiMode&Configuration.UI_MODE_TYPE_MASK)
-                != Configuration.UI_MODE_TYPE_TELEVISION) {
+        final int uiMode = getResources().getConfiguration().uiMode
+            & Configuration.UI_MODE_TYPE_MASK;
+        if (uiMode != Configuration.UI_MODE_TYPE_TELEVISION
+                && uiMode != Configuration.UI_MODE_TYPE_WATCH) {
             startSearch(null, false, null, false);
             return true;
         } else {
@@ -4213,6 +4216,9 @@
      * is to inject specific data such as context data, it is preferred to <i>override</i>
      * onSearchRequested(), so that any callers to it will benefit from the override.
      *
+     * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_WATCH}, use of this API is
+     * not supported.
+     *
      * @param initialQuery Any non-null non-empty string will be inserted as
      * pre-entered text in the search query box.
      * @param selectInitialQuery If true, the initial query will be preselected, which means that
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index eb26026..4a168fe 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -291,7 +291,10 @@
                 if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                     mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                 }
-                mDecor.setVisibility(View.VISIBLE);
+                if (mDecor.getVisibility() != View.VISIBLE) {
+                    mDecor.setVisibility(View.VISIBLE);
+                    sendShowMessage();
+                }
             }
             return;
         }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index ea990ad..4e2cb64 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -48,6 +48,9 @@
  * and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
  * {@link android.content.Intent Intent}.
  *
+ * <p>
+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support this system service.
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For more information about using the search dialog and adding search
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 19f6a49..16e36bc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3495,18 +3495,18 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      * @throws IllegalArgumentException if the input reason string is null or empty.
      */
-    public void wipeDataWithReason(int flags, @NonNull CharSequence reason) {
-        throwIfParentInstance("wipeDataWithReason");
+    public void wipeData(int flags, @NonNull CharSequence reason) {
+        throwIfParentInstance("wipeData");
         Preconditions.checkNotNull(reason, "CharSequence is null");
         wipeDataInternal(flags, reason.toString());
     }
 
     /**
      * Internal function for both {@link #wipeData(int)} and
-     * {@link #wipeDataWithReason(int, CharSequence)} to call.
+     * {@link #wipeData(int, CharSequence)} to call.
      *
      * @see #wipeData(int)
-     * @see #wipeDataWithReason(int, CharSequence)
+     * @see #wipeData(int, CharSequence)
      * @hide
      */
     private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e2c8662..ae1d8d7 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -24,6 +24,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
@@ -295,12 +296,10 @@
     }
 
     /**
-     * Turns a slice intent into a slice uri. Expects an explicit intent. If there is no
-     * {@link android.content.ContentProvider} associated with the given intent this will throw
-     * {@link IllegalArgumentException}.
+     * Turns a slice intent into a slice uri. Expects an explicit intent.
      *
      * @param intent The intent associated with a slice.
-     * @return The Slice Uri provided by the app or null if none is given.
+     * @return The Slice Uri provided by the app or null if none exists.
      * @see Slice
      * @see SliceProvider#onMapIntentToUri(Intent)
      * @see Intent
@@ -320,7 +319,16 @@
         List<ResolveInfo> providers =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
         if (providers == null || providers.isEmpty()) {
-            throw new IllegalArgumentException("Unable to resolve intent " + intent);
+            // There are no providers, see if this activity has a direct link.
+            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                    PackageManager.GET_META_DATA);
+            if (resolve != null && resolve.activityInfo != null
+                    && resolve.activityInfo.metaData != null
+                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+                return Uri.parse(
+                        resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+            }
+            return null;
         }
         String authority = providers.get(0).providerInfo.authority;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -371,7 +379,16 @@
         List<ResolveInfo> providers =
                 mContext.getPackageManager().queryIntentContentProviders(intent, 0);
         if (providers == null || providers.isEmpty()) {
-            throw new IllegalArgumentException("Unable to resolve intent " + intent);
+            // There are no providers, see if this activity has a direct link.
+            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                    PackageManager.GET_META_DATA);
+            if (resolve != null && resolve.activityInfo != null
+                    && resolve.activityInfo.metaData != null
+                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+                return bindSlice(Uri.parse(resolve.activityInfo.metaData
+                        .getString(SLICE_METADATA_KEY)), supportedSpecs);
+            }
+            return null;
         }
         String authority = providers.get(0).providerInfo.authority;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6b573e9..521ab4e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -109,7 +109,8 @@
         public static final int NOTIFICATION_SEEN = 10;
 
         /**
-         * An event type denoting a change in App Standby Bucket.
+         * An event type denoting a change in App Standby Bucket. Additional bucket information
+         * is contained in mBucketAndReason.
          * @hide
          */
         @SystemApi
@@ -180,11 +181,12 @@
         public String[] mContentAnnotations;
 
         /**
-         * The app standby bucket assigned.
+         * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
+         * is the low order 16 bits.
          * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
          * {@hide}
          */
-        public int mBucket;
+        public int mBucketAndReason;
 
         /** @hide */
         @EventFlags
@@ -205,7 +207,7 @@
             mContentType = orig.mContentType;
             mContentAnnotations = orig.mContentAnnotations;
             mFlags = orig.mFlags;
-            mBucket = orig.mBucket;
+            mBucketAndReason = orig.mBucketAndReason;
         }
 
         /**
@@ -268,7 +270,19 @@
          */
         @SystemApi
         public int getStandbyBucket() {
-            return mBucket;
+            return (mBucketAndReason & 0xFFFF0000) >>> 16;
+        }
+
+        /**
+         * Returns the reason for the bucketing, if the event is of type
+         * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
+         * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
+         * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
+         * is REASON_MAIN_USAGE.
+         * @hide
+         */
+        public int getStandbyReason() {
+            return mBucketAndReason & 0x0000FFFF;
         }
 
         /** @hide */
@@ -428,7 +442,7 @@
                 p.writeStringArray(event.mContentAnnotations);
                 break;
             case Event.STANDBY_BUCKET_CHANGED:
-                p.writeInt(event.mBucket);
+                p.writeInt(event.mBucketAndReason);
                 break;
         }
     }
@@ -474,7 +488,7 @@
                 eventOut.mContentAnnotations = p.createStringArray();
                 break;
             case Event.STANDBY_BUCKET_CHANGED:
-                eventOut.mBucket = p.readInt();
+                eventOut.mBucketAndReason = p.readInt();
                 break;
         }
     }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 9a0bb1d..5a57b06 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -132,24 +132,37 @@
     @SystemApi
     public static final int STANDBY_BUCKET_NEVER = 50;
 
-    /** {@hide} Reason for bucketing -- default initial state */
-    public static final String REASON_DEFAULT = "default";
+    /** @hide */
+    public static final int REASON_MAIN_MASK = 0xFF00;
+    /** @hide */
+    public static final int REASON_MAIN_DEFAULT =   0x0100;
+    /** @hide */
+    public static final int REASON_MAIN_TIMEOUT =   0x0200;
+    /** @hide */
+    public static final int REASON_MAIN_USAGE =     0x0300;
+    /** @hide */
+    public static final int REASON_MAIN_FORCED =    0x0400;
+    /** @hide */
+    public static final int REASON_MAIN_PREDICTED = 0x0500;
 
-    /** {@hide} Reason for bucketing -- timeout */
-    public static final String REASON_TIMEOUT = "timeout";
-
-    /** {@hide} Reason for bucketing -- usage */
-    public static final String REASON_USAGE = "usage";
-
-    /** {@hide} Reason for bucketing -- forced by user / shell command */
-    public static final String REASON_FORCED = "forced";
-
-    /**
-     * {@hide}
-     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
-     * be appended.
-     */
-    public static final String REASON_PREDICTED = "predicted";
+    /** @hide */
+    public static final int REASON_SUB_MASK = 0x00FF;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN  = 0x0002;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_USER_INTERACTION   = 0x0003;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYSTEM_UPDATE      = 0x0006;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT     = 0x0007;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SYNC_ADAPTER       = 0x0008;
 
     /** @hide */
     @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -427,6 +440,55 @@
         }
     }
 
+    /** @hide */
+    public static String reasonToString(int standbyReason) {
+        StringBuilder sb = new StringBuilder();
+        switch (standbyReason & REASON_MAIN_MASK) {
+            case REASON_MAIN_DEFAULT:
+                sb.append("d");
+                break;
+            case REASON_MAIN_FORCED:
+                sb.append("f");
+                break;
+            case REASON_MAIN_PREDICTED:
+                sb.append("p");
+                break;
+            case REASON_MAIN_TIMEOUT:
+                sb.append("t");
+                break;
+            case REASON_MAIN_USAGE:
+                sb.append("u-");
+                switch (standbyReason & REASON_SUB_MASK) {
+                    case REASON_SUB_USAGE_SYSTEM_INTERACTION:
+                        sb.append("si");
+                        break;
+                    case REASON_SUB_USAGE_NOTIFICATION_SEEN:
+                        sb.append("ns");
+                        break;
+                    case REASON_SUB_USAGE_USER_INTERACTION:
+                        sb.append("ui");
+                        break;
+                    case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
+                        sb.append("mf");
+                        break;
+                    case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
+                        sb.append("mb");
+                        break;
+                    case REASON_SUB_USAGE_SYSTEM_UPDATE:
+                        sb.append("su");
+                        break;
+                    case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
+                        sb.append("at");
+                        break;
+                    case REASON_SUB_USAGE_SYNC_ADAPTER:
+                        sb.append("sa");
+                        break;
+                }
+                break;
+        }
+        return sb.toString();
+    }
+
     /**
      * {@hide}
      * Temporarily whitelist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5d6a989..b62b1ee 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -139,7 +139,7 @@
 
         /** Callback to inform listeners that the idle state has changed to a new bucket. */
         public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
-                boolean idle, int bucket);
+                boolean idle, int bucket, int reason);
 
         /**
          * Callback to inform listeners that the parole state has changed. This means apps are
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f184380..e1a00b1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3370,7 +3370,11 @@
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.app.SearchManager} for handling searches.
      *
-     * @see #getSystemService(String)
+     * <p>
+     * {@link Configuration#UI_MODE_TYPE_WATCH} does not support
+     * {@link android.app.SearchManager}.
+     *
+     * @see #getSystemService
      * @see android.app.SearchManager
      */
     public static final String SEARCH_SERVICE = "search";
diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 0000000..62ab9e0
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+    /**
+     * Return optimization information about the application {@code info} when
+     * in executes using the specified {@code abi}.
+     */
+    public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+            ApplicationInfo info, String abi);
+}
diff --git a/core/java/android/content/pm/dex/PackageOptimizationInfo.java b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 0000000..b650457
--- /dev/null
+++ b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+    private final String mCompilationFilter;
+    private final String mCompilationReason;
+
+    public PackageOptimizationInfo(String compilerFilter, String compilationReason) {
+        this.mCompilationReason = compilationReason;
+        this.mCompilationFilter = compilerFilter;
+    }
+
+    public String getCompilationReason() {
+        return mCompilationReason;
+    }
+
+    public String getCompilationFilter() {
+        return mCompilationFilter;
+    }
+
+    /**
+     * Create a default optimization info object for the case when we have no information.
+     */
+    public static PackageOptimizationInfo createWithNoInfo() {
+        return new PackageOptimizationInfo("no-info", "no-info");
+    }
+}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index eb30979..93690bf 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -18,13 +18,27 @@
 
 import static android.content.ConfigurationProto.DENSITY_DPI;
 import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
 import static android.content.ConfigurationProto.ORIENTATION;
 import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
 import static android.content.ConfigurationProto.SCREEN_LAYOUT;
 import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
 import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
 import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
 import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -38,6 +52,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.proto.ProtoOutputStream;
 import android.view.View;
 
@@ -1076,7 +1091,19 @@
     public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
         final long token = protoOutputStream.start(fieldId);
         protoOutputStream.write(FONT_SCALE, fontScale);
+        protoOutputStream.write(MCC, mcc);
+        protoOutputStream.write(MNC, mnc);
+        mLocaleList.writeToProto(protoOutputStream, LOCALES);
         protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+        protoOutputStream.write(HDR_COLOR_MODE,
+                (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
+        protoOutputStream.write(WIDE_COLOR_GAMUT,
+                colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+        protoOutputStream.write(TOUCHSCREEN, touchscreen);
+        protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+        protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+        protoOutputStream.write(NAVIGATION, navigation);
+        protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
         protoOutputStream.write(ORIENTATION, orientation);
         protoOutputStream.write(UI_MODE, uiMode);
         protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1088,6 +1115,36 @@
     }
 
     /**
+     * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+     * stream.
+     *
+     * @param protoOutputStream Stream to write the Configuration object to.
+     * @param fieldId           Field Id of the Configuration as defined in the parent message
+     * @param metrics           Current display information
+     * @hide
+     */
+    public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+            DisplayMetrics metrics) {
+        final int width, height;
+        if (metrics.widthPixels >= metrics.heightPixels) {
+            width = metrics.widthPixels;
+            height = metrics.heightPixels;
+        } else {
+            //noinspection SuspiciousNameCombination
+            width = metrics.heightPixels;
+            //noinspection SuspiciousNameCombination
+            height = metrics.widthPixels;
+        }
+
+        final long token = protoOutputStream.start(fieldId);
+        writeToProto(protoOutputStream, CONFIGURATION);
+        protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+        protoOutputStream.write(SCREEN_WIDTH_PX, width);
+        protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+        protoOutputStream.end(token);
+    }
+
+    /**
      * Convert the UI mode to a human readable format.
      * @hide
      */
@@ -1925,11 +1982,21 @@
 
     /**
      * Returns a string representation of the configuration that can be parsed
-     * by build tools (like AAPT).
+     * by build tools (like AAPT), without display metrics included
      *
      * @hide
      */
     public static String resourceQualifierString(Configuration config) {
+        return resourceQualifierString(config, null);
+    }
+
+    /**
+     * Returns a string representation of the configuration that can be parsed
+     * by build tools (like AAPT).
+     *
+     * @hide
+     */
+    public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
         ArrayList<String> parts = new ArrayList<String>();
 
         if (config.mcc != 0) {
@@ -2177,6 +2244,20 @@
                 break;
         }
 
+        if (metrics != null) {
+            final int width, height;
+            if (metrics.widthPixels >= metrics.heightPixels) {
+                width = metrics.widthPixels;
+                height = metrics.heightPixels;
+            } else {
+                //noinspection SuspiciousNameCombination
+                width = metrics.heightPixels;
+                //noinspection SuspiciousNameCombination
+                height = metrics.widthPixels;
+            }
+            parts.add(width + "x" + height);
+        }
+
         parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
         return TextUtils.join("-", parts);
     }
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2425bba..66d92c4 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -98,6 +98,9 @@
 
     /** Accumulate a single netd sock_diag poll result reported by netd. */
     public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+        if (pendingSummary == null) {
+            pendingSummary = new Summary(netId, transports);
+        }
         pendingSummary.tcpLossRate.count(lost, sent);
         pendingSummary.roundTripTimeUs.count(rttUs);
         pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc46b73..5e9d39c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -447,6 +447,11 @@
          */
         public abstract LongCounter getScanTimeCounter();
 
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * sleep state.
+         */
+        public abstract LongCounter getSleepTimeCounter();
 
         /**
          * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
@@ -3408,8 +3413,6 @@
         for (LongCounter txState : counter.getTxTimeCounters()) {
             totalTxTimeMs += txState.getCountLocked(which);
         }
-        final long sleepTimeMs
-            = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
 
         if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
             final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
@@ -3423,18 +3426,34 @@
             sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
             sb.append(")");
             pw.println(sb.toString());
+
+            final long sleepTimeMs
+                = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
         }
 
-        sb.setLength(0);
-        sb.append(prefix);
-        sb.append("     ");
-        sb.append(controllerName);
-        sb.append(" Sleep time:  ");
-        formatTimeMs(sb, sleepTimeMs);
-        sb.append("(");
-        sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
-        sb.append(")");
-        pw.println(sb.toString());
+        if (controllerName.equals(CELLULAR_CONTROLLER_NAME)) {
+            final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Sleep time:  ");
+            formatTimeMs(sb, sleepTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
 
         sb.setLength(0);
         sb.append(prefix);
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index fc88e90..0417ded 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -683,6 +683,23 @@
         return enqueueMessage(queue, msg, 0);
     }
 
+    /**
+     * Executes the message synchronously if called on the same thread this handler corresponds to,
+     * or {@link #sendMessage pushes it to the queue} otherwise
+     *
+     * @return Returns true if the message was successfully ran or placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     * @hide
+     */
+    public final boolean executeOrSendMessage(Message msg) {
+        if (mLooper == Looper.myLooper()) {
+            dispatchMessage(msg);
+            return true;
+        }
+        return sendMessage(msg);
+    }
+
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
         msg.target = this;
         if (mAsynchronous) {
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index ca9cbec..87e1b7d 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,7 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.content.LocaleProto;
 import android.icu.util.ULocale;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -140,6 +142,25 @@
     }
 
     /**
+     * Helper to write LocaleList to a protocol buffer output stream.  Assumes the parent
+     * protobuf has declared the locale as repeated.
+     *
+     * @param protoOutputStream Stream to write the locale to.
+     * @param fieldId Field Id of the Locale as defined in the parent message.
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        for (int i = 0; i < mList.length; i++) {
+            final Locale locale = mList[i];
+            final long token = protoOutputStream.start(fieldId);
+            protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
+            protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
+            protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
+            protoOutputStream.end(token);
+        }
+    }
+
+    /**
      * Retrieves a String representation of the language tags in this list.
      */
     @NonNull
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 3e8161f..d4d3dc8 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -46,16 +46,17 @@
      * @param tag    The integer representing the tag for this event.
      * @param fields The number of fields specified in this event.
      */
-    public StatsLogEventWrapper(int tag, int fields) {
+    public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
         // Write four bytes from tag, starting with least-significant bit.
         // 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 + 1); // Indicate number of elements in this list. +1 for the tag
-        mStorage.write(EVENT_TYPE_INT);
-        // The first element is the real atom tag number
-        write4Bytes(tag);
+        mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
+        // The first element is the elapsed realtime.
+        writeLong(elapsedNanos);
+        // The second element is the real atom tag number
+        writeInt(tag);
     }
 
     /**
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f90604a..76c13be 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -636,7 +636,7 @@
              * executor every violation.
              */
             public Builder penaltyListener(
-                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                    @NonNull Executor executor, @NonNull OnThreadViolationListener listener) {
                 if (executor == null) {
                     throw new NullPointerException("executor must not be null");
                 }
@@ -645,6 +645,12 @@
                 return this;
             }
 
+            /** @removed */
+            public Builder penaltyListener(
+                    @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
@@ -971,7 +977,7 @@
              * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
              */
             public Builder penaltyListener(
-                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                    @NonNull Executor executor, @NonNull OnVmViolationListener listener) {
                 if (executor == null) {
                     throw new NullPointerException("executor must not be null");
                 }
@@ -980,6 +986,12 @@
                 return this;
             }
 
+            /** @removed */
+            public Builder penaltyListener(
+                    @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+                return penaltyListener(executor, listener);
+            }
+
             private Builder enable(int bit) {
                 mMask |= bit;
                 return this;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1ae0bc4..0442c9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9085,14 +9085,28 @@
          */
         public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
 
-       /**
-        * List of carrier apps which are whitelisted to prompt the user for install when
-        * a sim card with matching uicc carrier privilege rules is inserted.
-        *
-        * The value is "package1;package2;..."
-        * @hide
-        */
-       public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+        /**
+         * List of certificate (hex string representation of the application's certificate - SHA-1
+         * or SHA-256) and carrier app package pairs which are whitelisted to prompt the user for
+         * install when a sim card with matching UICC carrier privilege rules is inserted.  The
+         * certificate is used as a key, so the certificate encoding here must be the same as the
+         * certificate encoding used on the SIM.
+         *
+         * The value is "cert1:package1;cert2:package2;..."
+         * @hide
+         */
+        @SystemApi
+        public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+
+        /**
+         * Map of package name to application names.  Package names must be lower cased as they are
+         * used as a key in the map.  The application names cannot and will not be localized.
+         *
+         * The value is "packageName1:appName1;packageName2:appName2;..."
+         * @hide
+         */
+        @SystemApi
+        public static final String CARRIER_APP_NAMES = "carrier_app_names";
 
        /**
         * USB Mass Storage Enabled
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index 3a99d63..e0c6e04 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -21,6 +21,7 @@
 
 /**
  * Interface to receive call-backs when the service is connected.
+ * @hide
  */
 interface ISecureElementListener {
   /**
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index b8937e6..d59e86a 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -59,6 +59,21 @@
      */
     public static final int NO_SUCH_ELEMENT_ERROR = 2;
 
+    /**
+     * Interface to send call-backs to the application when the service is connected.
+     */
+    public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+        @Override
+        public IBinder asBinder() {
+            return this;
+        }
+
+        /**
+         * Called by the framework when the service is connected.
+         */
+        public void serviceConnected() {};
+    }
+
     private static final String TAG = "OMAPI.SEService";
 
     private final Object mLock = new Object();
@@ -98,9 +113,9 @@
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a ISecureElementListener object. Can be <code>null</code>.
+     *            a SecureElementListener object. Can be <code>null</code>.
      */
-    public SEService(Context context, ISecureElementListener listener) {
+    public SEService(Context context, SecureElementListener listener) {
 
         if (context == null) {
             throw new NullPointerException("context must not be null");
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 786d454..4a0de5f 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -291,7 +291,7 @@
             // IPC doesn't support generic Maps.
             @SuppressWarnings("unchecked")
             Map<String, Integer> result =
-                    (Map<String, Integer>) mBinder.getRecoveryStatus(/*packageName=*/ null);
+                    (Map<String, Integer>) mBinder.getRecoveryStatus();
             return result;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index df535ed..d4ec472 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -23,6 +23,7 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.security.cert.CertPath;
 import java.util.List;
 
 /**
@@ -52,7 +53,8 @@
     private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS;
     private long mCounterId = DEFAULT_COUNTER_ID;
     private byte[] mServerParams;
-    private byte[] mPublicKey;
+    private byte[] mPublicKey;  // The raw public key bytes used
+    private CertPath mCertPath;  // The certificate path including the intermediate certificates
     private List<KeyChainProtectionParams> mKeyChainProtectionParams;
     private List<WrappedApplicationKey> mEntryRecoveryData;
     private byte[] mEncryptedRecoveryKeyBlob;
@@ -111,14 +113,25 @@
     /**
      * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
      *
-     * See implementation for binary key format
+     * See implementation for binary key format.
+     *
+     * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
+     * @removed
      */
-    // TODO: document key format.
+    @Deprecated
     public @NonNull byte[] getTrustedHardwarePublicKey() {
         return mPublicKey;
     }
 
     /**
+     * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
+     */
+    // TODO: Change to @NonNull
+    public CertPath getTrustedHardwareCertPath() {
+        return mCertPath;
+    }
+
+    /**
      * UI and key derivation parameters. Note that combination of secrets may be used.
      */
     public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() {
@@ -207,13 +220,29 @@
          *
          * @param publicKey The public key
          * @return This builder.
+         * @deprecated Use {@link #setTrustedHardwareCertPath} instead.
+         * @removed
          */
+        @Deprecated
         public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
             mInstance.mPublicKey = publicKey;
             return this;
         }
 
         /**
+         * Sets CertPath used to validate the trusted hardware public key. The CertPath should
+         * contain a certificate of the trusted hardware public key and any necessary intermediate
+         * certificates.
+         *
+         * @param certPath The public key
+         * @return This builder.
+         */
+        public Builder setTrustedHardwareCertPath(CertPath certPath) {
+            mInstance.mCertPath = certPath;
+            return this;
+        }
+
+        /**
          * Sets UI and key derivation parameters
          *
          * @param recoveryMetadata The UI and key derivation parameters
diff --git a/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl b/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl
new file mode 100644
index 0000000..c171a92
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.recovery;
+
+/* @hide */
+parcelable RecoveryCertPath;
diff --git a/core/java/android/security/keystore/recovery/RecoveryCertPath.java b/core/java/android/security/keystore/recovery/RecoveryCertPath.java
new file mode 100644
index 0000000..1950947
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryCertPath.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+/**
+ * The certificate path of the recovery service.
+ *
+ * @hide
+ */
+public final class RecoveryCertPath implements Parcelable {
+
+    private static final String CERT_PATH_ENCODING = "PkiPath";
+
+    private final byte[] mEncodedCertPath;
+
+    /**
+     * Wraps a {@code CertPath} to create a {@code Parcelable} for Binder calls.
+     *
+     * @param certPath The certificate path to be wrapped.
+     * @throws CertificateException if the given certificate path cannot be encoded properly.
+     */
+    public static RecoveryCertPath createRecoveryCertPath(@NonNull CertPath certPath)
+            throws CertificateException {
+        // Perform the encoding here to avoid throwing exceptions in writeToParcel
+        try {
+            return new RecoveryCertPath(encodeCertPath(certPath));
+        } catch (CertificateEncodingException e) {
+            throw new CertificateException("Failed to encode the given CertPath", e);
+        }
+    }
+
+    /**
+     * Obtains the {@code CertPath} wrapped in the Parcelable.
+     *
+     * @return the wrapped certificate path.
+     * @throws CertificateException if the wrapped certificate path cannot be decoded properly.
+     */
+    public CertPath getCertPath() throws CertificateException {
+        // Perform the decoding here to avoid throwing exceptions in createFromParcel
+        return decodeCertPath(mEncodedCertPath);
+    }
+
+    private RecoveryCertPath(@NonNull byte[] encodedCertPath) {
+        mEncodedCertPath = Preconditions.checkNotNull(encodedCertPath);
+    }
+
+    private RecoveryCertPath(Parcel in) {
+        mEncodedCertPath = in.createByteArray();
+    }
+
+    public static final Parcelable.Creator<RecoveryCertPath> CREATOR =
+            new Parcelable.Creator<RecoveryCertPath>() {
+        public RecoveryCertPath createFromParcel(Parcel in) {
+            return new RecoveryCertPath(in);
+        }
+
+        public RecoveryCertPath[] newArray(int length) {
+            return new RecoveryCertPath[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(mEncodedCertPath);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @NonNull
+    private static byte[] encodeCertPath(@NonNull CertPath certPath)
+            throws CertificateEncodingException {
+        Preconditions.checkNotNull(certPath);
+        return certPath.getEncoded(CERT_PATH_ENCODING);
+    }
+
+    @NonNull
+    private static CertPath decodeCertPath(@NonNull byte[] bytes) throws CertificateException {
+        Preconditions.checkNotNull(bytes);
+        CertificateFactory certFactory;
+        try {
+            certFactory = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            // Should not happen, as X.509 is mandatory for all providers.
+            throw new RuntimeException(e);
+        }
+        return certFactory.generateCertPath(new ByteArrayInputStream(bytes), CERT_PATH_ENCODING);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index fcca5af..3389238 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -175,28 +175,13 @@
     }
 
     /**
-     * Deprecated - use getKeyChainSnapshot.
-     *
-     * Returns data necessary to store all recoverable keys. Key material is
-     * encrypted with user secret and recovery public key.
-     *
-     * @return Data necessary to recover keystore.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #getKeyChainSnapshot()}
+     * @removed
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
-    public @Nullable KeyChainSnapshot getRecoveryData()
-            throws InternalRecoveryServiceException {
-        try {
-            return mBinder.getKeyChainSnapshot();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) {
-                return null;
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+    public @Nullable KeyChainSnapshot getRecoveryData() throws InternalRecoveryServiceException {
+        return getKeyChainSnapshot();
     }
 
     /**
@@ -268,17 +253,21 @@
     }
 
     /**
-     * Gets aliases of recoverable keys for the application.
-     *
-     * @param packageName which recoverable keys' aliases will be returned.
-     *
-     * @return {@code List} of all aliases.
+     * @deprecated Use {@link #getAliases()}.
+     * @removed
      */
+    @Deprecated
     public List<String> getAliases(@Nullable String packageName)
             throws InternalRecoveryServiceException {
+        return getAliases();
+    }
+
+    /**
+     * Returns a list of aliases of keys belonging to the application.
+     */
+    public List<String> getAliases() throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             return new ArrayList<>(allStatuses.keySet());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -323,28 +312,31 @@
     }
 
     /**
-     * Returns recovery status for Application's KeyStore key.
-     * Negative status values are reserved for recovery agent specific codes. List of common codes:
+     * @deprecated Use {@link #getRecoveryStatus(String)}.
+     * @removed
+     */
+    @Deprecated
+    public int getRecoveryStatus(String packageName, String alias)
+            throws InternalRecoveryServiceException {
+        return getRecoveryStatus(alias);
+    }
+
+    /**
+     * Returns the recovery status for the key with the given {@code alias}.
      *
      * <ul>
      *   <li>{@link #RECOVERY_STATUS_SYNCED}
      *   <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
-     *   <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
      *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
      * </ul>
      *
-     * @param packageName Application whose recoverable key status is returned.
-     * @param alias Application-specific key alias.
-     * @return Recovery status.
-     * @see #setRecoveryStatus
+     * @see #setRecoveryStatus(String, int)
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
-    public int getRecoveryStatus(String packageName, String alias)
-            throws InternalRecoveryServiceException {
+    public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
             Integer status = allStatuses.get(alias);
             if (status == null) {
                 return RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 4db5d6e..069af91 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import java.security.SecureRandom;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.List;
 import java.util.Map;
@@ -91,7 +92,10 @@
      *     format.
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
+     * @deprecated Use {@link #start(CertPath, byte[], byte[], List)} instead.
+     * @removed
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     @NonNull public byte[] start(
             @NonNull byte[] verifierPublicKey,
@@ -119,6 +123,55 @@
     }
 
     /**
+     * Starts a recovery session and returns a blob with proof of recovery secret possession.
+     * The method generates a symmetric key for a session, which trusted remote device can use to
+     * return recovery key.
+     *
+     * @param verifierCertPath The certificate path used to create the recovery blob on the source
+     *     device. Keystore will verify the certificate path by using the root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     *     Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     *     replay attacks
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+     *     key and parameters necessary to identify the counter with the number of failed recovery
+     *     attempts.
+     * @throws CertificateException if the {@code verifierCertPath} is invalid.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    @NonNull public byte[] start(
+            @NonNull CertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+        RecoveryCertPath recoveryCertPath =
+                RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+                            mSessionId,
+                            recoveryCertPath,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
+                throw new CertificateException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
      * Imports keys.
      *
      * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 99e2c62..2b114d5 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -665,6 +665,11 @@
     }
 
     private void updateDoze() {
+        if (mWindowToken == null) {
+            Slog.w(TAG, "Updating doze without a window token.");
+            return;
+        }
+
         if (mDozing) {
             try {
                 mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9b4ea33..5966a86 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -137,7 +137,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 175 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 176 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -2823,6 +2823,7 @@
             implements Parcelable {
         private final LongSamplingCounter mIdleTimeMillis;
         private final LongSamplingCounter mScanTimeMillis;
+        private final LongSamplingCounter mSleepTimeMillis;
         private final LongSamplingCounter mRxTimeMillis;
         private final LongSamplingCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
@@ -2830,6 +2831,7 @@
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase);
             mScanTimeMillis = new LongSamplingCounter(timeBase);
+            mSleepTimeMillis = new LongSamplingCounter(timeBase);
             mRxTimeMillis = new LongSamplingCounter(timeBase);
             mTxTimeMillis = new LongSamplingCounter[numTxStates];
             for (int i = 0; i < numTxStates; i++) {
@@ -2841,6 +2843,7 @@
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
             mScanTimeMillis = new LongSamplingCounter(timeBase, in);
+            mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
             mRxTimeMillis = new LongSamplingCounter(timeBase, in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != numTxStates) {
@@ -2857,6 +2860,7 @@
         public void readSummaryFromParcel(Parcel in) {
             mIdleTimeMillis.readSummaryFromParcelLocked(in);
             mScanTimeMillis.readSummaryFromParcelLocked(in);
+            mSleepTimeMillis.readSummaryFromParcelLocked(in);
             mRxTimeMillis.readSummaryFromParcelLocked(in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != mTxTimeMillis.length) {
@@ -2876,6 +2880,7 @@
         public void writeSummaryToParcel(Parcel dest) {
             mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
             mScanTimeMillis.writeSummaryFromParcelLocked(dest);
+            mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
             mRxTimeMillis.writeSummaryFromParcelLocked(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2888,6 +2893,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             mIdleTimeMillis.writeToParcel(dest);
             mScanTimeMillis.writeToParcel(dest);
+            mSleepTimeMillis.writeToParcel(dest);
             mRxTimeMillis.writeToParcel(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2899,6 +2905,7 @@
         public void reset(boolean detachIfReset) {
             mIdleTimeMillis.reset(detachIfReset);
             mScanTimeMillis.reset(detachIfReset);
+            mSleepTimeMillis.reset(detachIfReset);
             mRxTimeMillis.reset(detachIfReset);
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.reset(detachIfReset);
@@ -2909,6 +2916,7 @@
         public void detach() {
             mIdleTimeMillis.detach();
             mScanTimeMillis.detach();
+            mSleepTimeMillis.detach();
             mRxTimeMillis.detach();
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.detach();
@@ -2935,6 +2943,15 @@
         }
 
         /**
+         * @return a LongSamplingCounter, measuring time spent in the sleep state in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter getSleepTimeCounter() {
+            return mSleepTimeMillis;
+        }
+
+        /**
          * @return a LongSamplingCounter, measuring time spent in the receive state in
          * milliseconds.
          */
@@ -11438,6 +11455,8 @@
                 mHasModemReporting = true;
                 mModemActivity.getIdleTimeCounter().addCountLocked(
                         deltaInfo.getIdleTimeMillis());
+                mModemActivity.getSleepTimeCounter().addCountLocked(
+                        deltaInfo.getSleepTimeMillis());
                 mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis());
                 for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
                     mModemActivity.getTxTimeCounters()[lvl]
@@ -12960,6 +12979,7 @@
         final int which = STATS_SINCE_CHARGED;
         final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
         final ControllerActivityCounter counter = getModemControllerActivity();
+        final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
@@ -12979,10 +12999,6 @@
             txTimeMs[i] = counter.getTxTimeCounters()[i].getCountLocked(which);
             totalTxTimeMs += txTimeMs[i];
         }
-        final long totalControllerActivityTimeMs
-            = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
-        final long sleepTimeMs
-            = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
         s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
         s.setKernelActiveTimeMs(getMobileRadioActiveTime(rawRealTime, which) / 1000);
         s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
@@ -14602,6 +14618,7 @@
             u.mJobsFreshnessTimeMs.writeSummaryFromParcelLocked(out);
             for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
                 if (u.mJobsFreshnessBuckets[i] != null) {
+                    out.writeInt(1);
                     u.mJobsFreshnessBuckets[i].writeSummaryFromParcelLocked(out);
                 } else {
                     out.writeInt(0);
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 7178ec7..46667d1 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -16,6 +16,7 @@
 package com.android.internal.os;
 
 import android.os.Process;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Slog;
 
@@ -69,6 +70,7 @@
         boolean wakeup_sources;
         final long startTime = SystemClock.uptimeMillis();
 
+        final int oldMask = StrictMode.allowThreadDiskReadsMask();
         try {
             FileInputStream is;
             try {
@@ -90,6 +92,8 @@
         } catch (java.io.IOException e) {
             Slog.wtf(TAG, "failed to read kernel wakelocks", e);
             return null;
+        } finally {
+            StrictMode.setThreadPolicyMask(oldMask);
         }
 
         final long readTime = SystemClock.uptimeMillis() - startTime;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 03a7cd2..50c9d6c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2040,6 +2040,10 @@
                 if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
                     break;
                 }
+                if ((getContext().getResources().getConfiguration().uiMode
+                        & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH) {
+                    break;
+                }
                 if (event.isTracking() && !event.isCanceled()) {
                     launchDefaultSearch(event);
                 }
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 17e498c..1fc2796 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -22,6 +22,7 @@
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -73,7 +74,7 @@
     Map getRecoverySnapshotVersions();
     void setServerParams(in byte[] serverParams);
     void setRecoveryStatus(in String alias, int status);
-    Map getRecoveryStatus(in String packageName);
+    Map getRecoveryStatus();
     void setRecoverySecretTypes(in int[] secretTypes);
     int[] getRecoverySecretTypes();
     int[] getPendingRecoverySecretTypes();
@@ -81,6 +82,9 @@
     byte[] startRecoverySession(in String sessionId,
             in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
             in List<KeyChainProtectionParams> secrets);
+    byte[] startRecoverySessionWithCertPath(in String sessionId,
+            in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge,
+            in List<KeyChainProtectionParams> secrets);
     Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 7a248f2..51dd929 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -125,6 +125,7 @@
     private boolean mInStealthMode = false;
     private boolean mEnableHapticFeedback = true;
     private boolean mPatternInProgress = false;
+    private boolean mFadePattern = true;
 
     private float mHitFactor = 0.6f;
 
@@ -376,6 +377,14 @@
     }
 
     /**
+     * Set whether the pattern should fade as it's being drawn. If
+     * true, each segment of the pattern fades over time.
+     */
+    public void setFadePattern(boolean fadePattern) {
+        mFadePattern = fadePattern;
+    }
+
+    /**
      * Set whether the view will use tactile feedback.  If true, there will be
      * tactile feedback as the user enters the pattern.
      *
@@ -1167,10 +1176,18 @@
                     currentPath.moveTo(lastX, lastY);
                     if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
                         currentPath.lineTo(state.lineEndX, state.lineEndY);
-                        mPathPaint.setAlpha((int) 255 - lineFadeVal );
+                        if (mFadePattern) {
+                            mPathPaint.setAlpha((int) 255 - lineFadeVal );
+                        } else {
+                            mPathPaint.setAlpha(255);
+                        }
                     } else {
                         currentPath.lineTo(centerX, centerY);
-                        mPathPaint.setAlpha((int) 255 - lineFadeVal );
+                        if (mFadePattern) {
+                            mPathPaint.setAlpha((int) 255 - lineFadeVal );
+                        } else {
+                            mPathPaint.setAlpha(255);
+                        }
                     }
                     canvas.drawPath(currentPath, mPathPaint);
                 }
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index ba56d59..6fd4abf 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -132,6 +132,11 @@
 // Java's LOOP_INFINITE relies on this being the same.
 static_assert(SkCodec::kRepetitionCountInfinite == -1);
 
+static jint AnimatedImageDrawable_nGetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    return drawable->getRepetitionCount();
+}
+
 static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                                 jint loopCount) {
     auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
@@ -218,6 +223,7 @@
     { "nIsRunning",          "(J)Z",                                                         (void*) AnimatedImageDrawable_nIsRunning },
     { "nStart",              "(J)Z",                                                         (void*) AnimatedImageDrawable_nStart },
     { "nStop",               "(J)Z",                                                         (void*) AnimatedImageDrawable_nStop },
+    { "nGetLoopCount",       "(J)I",                                                         (void*) AnimatedImageDrawable_nGetLoopCount },
     { "nSetLoopCount",       "(JI)V",                                                        (void*) AnimatedImageDrawable_nSetLoopCount },
     { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
     { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
index 9198cbe..5bd808b 100644
--- a/core/jni/android_media_MicrophoneInfo.cpp
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -65,13 +65,11 @@
     }
     jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
                                         gMicrophoneInfoCoordinateCstor,
-                                        NULL,
                                         microphoneInfo->getGeometricLocation()[0],
                                         microphoneInfo->getGeometricLocation()[1],
                                         microphoneInfo->getGeometricLocation()[2]);
     jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
                                   gMicrophoneInfoCoordinateCstor,
-                                  NULL,
                                   microphoneInfo->getOrientation()[0],
                                   microphoneInfo->getOrientation()[1],
                                   microphoneInfo->getOrientation()[2]);
@@ -177,7 +175,7 @@
             env, "android/media/MicrophoneInfo$Coordinate3F");
     gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass);
     gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>",
-           "(Landroid/media/MicrophoneInfo;FFF)V");
+           "(FFF)V");
 
     jclass pairClass = FindClassOrDie(env, "android/util/Pair");
     gPairClass = MakeGlobalRefOrDie(env, pairClass);
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index a62d56c..834ecde 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -25,7 +25,7 @@
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 /**
- * An android resource configuration.
+ * An android Configuration object.
  */
 message ConfigurationProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -35,17 +35,65 @@
   optional uint32 mnc = 3;
   repeated LocaleProto locales = 4;
   optional uint32 screen_layout = 5;
-  optional uint32 touchscreen = 6;
-  optional uint32 keyboard_hidden = 7;
-  optional uint32 hard_keyboard_hidden = 8;
-  optional uint32 navigation = 9;
-  optional uint32 navigation_hidden = 10;
-  optional uint32 orientation = 11;
-  optional uint32 ui_mode = 12;
-  optional uint32 screen_width_dp = 13;
-  optional uint32 screen_height_dp = 14;
-  optional uint32 smallest_screen_width_dp = 15;
-  optional uint32 density_dpi = 16;
-  optional .android.app.WindowConfigurationProto window_configuration = 17;
+  optional uint32 hdr_color_mode = 6;
+  optional uint32 wide_color_gamut = 7;
+  optional uint32 touchscreen = 8;
+  optional uint32 keyboard_hidden = 9;
+  optional uint32 hard_keyboard_hidden = 10;
+  optional uint32 navigation = 11;
+  optional uint32 navigation_hidden = 12;
+  optional uint32 orientation = 13;
+  optional uint32 ui_mode = 14;
+  optional uint32 screen_width_dp = 15;
+  optional uint32 screen_height_dp = 16;
+  optional uint32 smallest_screen_width_dp = 17;
+  optional uint32 density_dpi = 18;
+  optional .android.app.WindowConfigurationProto window_configuration = 19;
 }
 
+/**
+ * All current configuration data used to select resources.
+ */
+message ResourcesConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  required ConfigurationProto configuration = 1;
+
+  optional uint32 sdk_version = 2;
+  optional uint32 screen_width_px = 3;
+  optional uint32 screen_height_px = 4;
+}
+
+/**
+ * Overall device configuration data.
+ */
+message DeviceConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional uint32 stable_screen_width_px = 1;
+  optional uint32 stable_screen_height_px = 2;
+  optional uint32 stable_density_dpi = 3;
+
+  optional uint64 total_ram = 4;
+  optional bool low_ram = 5;
+  optional uint32 max_cores = 6;
+  optional bool has_secure_screen_lock = 7;
+
+  optional uint32 opengl_version = 8;
+  repeated string opengl_extensions = 9;
+
+  repeated string shared_libraries = 10;
+  repeated string features = 11;
+  repeated string cpu_architectures = 12;
+}
+
+/**
+ * All current configuration data device is running with, everything used
+ * to filter and target apps.
+ */
+message GlobalConfigurationProto {
+  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional ResourcesConfigurationProto resources = 1;
+  optional DeviceConfigurationProto device = 2;
+}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index c35b2ec..9752d3b 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -179,9 +179,11 @@
     optional SettingProto tether_dun_required = 114 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto tether_dun_apn = 115;
     optional SettingProto tether_offload_disabled = 301 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // List of carrier apps which are whitelisted to prompt the user for install
-    // when a SIM card with marchin UICC carrier privilege rules is inserted.
+    // List of carrier app certificate mapped to carrier app package id which are whitelisted to
+    // prompt the user for install when a SIM card with matching UICC carrier privilege rules is
+    // inserted.
     optional SettingProto carrier_app_whitelist = 116 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto carrier_app_names = 358 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto usb_mass_storage_enabled = 117 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto use_google_mail = 118 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto webview_data_reduction_proxy_key = 119;
@@ -433,7 +435,7 @@
 
     // Please insert fields in the same order as in
     // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 356;
+    // Next tag = 359;
 }
 
 message SecureSettingsProto {
diff --git a/core/res/res/drawable-watch/sym_def_app_icon.xml b/core/res/res/drawable-watch/sym_def_app_icon.xml
new file mode 100644
index 0000000..6945256
--- /dev/null
+++ b/core/res/res/drawable-watch/sym_def_app_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/sym_def_app_icon_background" />
+    <foreground android:drawable="@mipmap/sym_def_app_icon_foreground" />
+</adaptive-icon>
diff --git a/core/res/res/drawable-watch/sym_def_app_icon_background.xml b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
new file mode 100644
index 0000000..6d6352f
--- /dev/null
+++ b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#2374CE"/>
+</shape>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 525d493..346aad6c 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -43,6 +43,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
+            android:layout_weight="1"
             android:paddingStart="@dimen/notification_content_margin_start"
             android:paddingEnd="@dimen/notification_content_margin_end"
             android:clipToPadding="false"
@@ -75,19 +76,19 @@
                 android:maxLines="3"
             />
         </LinearLayout>
+        <FrameLayout android:id="@+id/actions_container"
+                     android:layout_width="match_parent"
+                     android:layout_height="wrap_content"
+                     android:layout_gravity="bottom">
+            <com.android.internal.widget.NotificationActionListLayout
+                android:id="@+id/actions"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/notification_action_list_height"
+                android:paddingEnd="4dp"
+                android:orientation="horizontal"
+                android:gravity="center"
+                android:visibility="gone"
+            />
+        </FrameLayout>
     </LinearLayout>
-    <FrameLayout android:id="@+id/actions_container"
-                 android:layout_width="match_parent"
-                 android:layout_height="wrap_content"
-                 android:layout_gravity="bottom">
-        <com.android.internal.widget.NotificationActionListLayout
-            android:id="@+id/actions"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_action_list_height"
-            android:paddingEnd="4dp"
-            android:orientation="horizontal"
-            android:gravity="center"
-            android:visibility="gone"
-        />
-    </FrameLayout>
 </FrameLayout>
diff --git a/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
new file mode 100644
index 0000000..69c241c
--- /dev/null
+++ b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="24dp"
+    android:insetRight="24dp"
+    android:insetTop="24dp"
+    android:insetBottom="24dp">
+    <vector
+        android:width="60dp"
+        android:height="60dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M20,12c0,-2.6 -1.2,-4.8 -3.1,-6.3l-0.5,-2.8c-1.4,-0.7 -2.9,-1.1 -4.5,-1.1c-1.6,0 -3.1,0.4 -4.5,1.1L7,5.7C5.1,7.2 3.9,9.4 3.9,12c0,2.6 1.2,4.8 3.1,6.3l0.5,2.8c1.4,0.7 2.9,1.1 4.5,1.1c1.6,0 3.2,-0.4 4.5,-1.1l0.5,-2.8C18.8,16.8 20,14.6 20,12zM12,18c-3.3,0 -6,-2.7 -6,-6c0,-3.3 2.7,-6 6,-6s6,2.7 6,6C18,15.3 15.3,18 12,18z"/>
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M11.2,15.6h1.4v-4.3h-1.4V15.6zM11.2,9.8h1.4V8.4h-1.4V9.8z"/>
+    </vector>
+</inset>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0b33211..96a83f8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8006,6 +8006,8 @@
         <attr name="icon"/>
         <!-- The activity to launch when the setting is clicked on. -->
         <attr name="settingsActivity"/>
+        <!-- The user restriction for this preference. -->
+        <attr name="userRestriction"/>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 82fefef..a5ba4c6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2870,6 +2870,8 @@
       <public name="outlineAmbientShadowColor" />
       <public name="maxLongVersionCode" />
       <public name="urlBarResourceId" />
+      <!-- @hide @SystemApi -->
+      <public name="userRestriction" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cadc3ff..5c9f863 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3204,9 +3204,10 @@
     <string name="sim_restart_button">Restart</string>
     <!-- See Carrier_App_Dialog. This is the message of that dialog. -->
     <string name="install_carrier_app_notification_title">Activate mobile service</string>
-    <string name="install_carrier_app_notification_text">
-        Download the carrier app to activate your new SIM
-    </string>
+    <!-- Notification message that shows when the user inserts a SIM card that requires a carrier app download, but the app name is unknown -->
+    <string name="install_carrier_app_notification_text">Download the carrier app to activate your new SIM</string>
+    <!-- Notification message that shows when the user inserts a SIM card that requires a carrier app download.  App name is known -->
+    <string name="install_carrier_app_notification_text_app_name">Download the <xliff:g id="app_name">%1$s</xliff:g> app to activate your new SIM</string>
     <!-- See Carrier_App_Notification. This is the button of that dialog. -->
     <string name="install_carrier_app_notification_button">Download app</string>
     <!-- See carrier_app_notification. This is the headline. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a2af57e..a6a3663 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2774,6 +2774,7 @@
 
   <java-symbol type="string" name="install_carrier_app_notification_title" />
   <java-symbol type="string" name="install_carrier_app_notification_text" />
+  <java-symbol type="string" name="install_carrier_app_notification_text_app_name" />
   <java-symbol type="string" name="install_carrier_app_notification_button" />
   <java-symbol type="string" name="carrier_app_notification_title" />
   <java-symbol type="string" name="carrier_app_notification_text" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bba8c1a..be2c235 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -155,6 +155,7 @@
                     Settings.Global.CAPTIVE_PORTAL_USER_AGENT,
                     Settings.Global.CAR_DOCK_SOUND,
                     Settings.Global.CARRIER_APP_WHITELIST,
+                    Settings.Global.CARRIER_APP_NAMES,
                     Settings.Global.CAR_UNDOCK_SOUND,
                     Settings.Global.CDMA_CELL_BROADCAST_SMS,
                     Settings.Global.CDMA_ROAMING_MODE,
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 796d008..f70d6e1 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -16,8 +16,6 @@
 
 package android.graphics.drawable;
 
-import dalvik.annotation.optimization.FastNative;
-
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -25,8 +23,6 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.InflateException;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
@@ -35,20 +31,21 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
 import com.android.internal.R;
 
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import libcore.io.IoUtils;
-import libcore.util.NativeAllocationRegistry;
-
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.Runnable;
 import java.util.ArrayList;
 
 /**
@@ -109,6 +106,16 @@
     }
 
     /**
+     * Retrieve the number of times the animation will loop.
+     */
+    public int getLoopCount() {
+        if (mState.mNativePtr == 0) {
+            throw new IllegalStateException("called getLoopCount on empty AnimatedImageDrawable");
+        }
+        return nGetLoopCount(mState.mNativePtr);
+    }
+
+    /**
      * Create an empty AnimatedImageDrawable.
      */
     public AnimatedImageDrawable() {
@@ -501,6 +508,8 @@
     @FastNative
     private static native boolean nStop(long nativePtr);
     @FastNative
+    private static native int nGetLoopCount(long nativePtr);
+    @FastNative
     private static native void nSetLoopCount(long nativePtr, int loopCount);
     // Pass the drawable down to native so it can call onAnimationEnd.
     private static native void nSetOnAnimationEndListener(long nativePtr,
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 07469d2..a5260be 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -72,6 +72,7 @@
     // already stopped)
     bool stop();
     bool isRunning();
+    int getRepetitionCount() const { return mSkAnimatedImage->getRepetitionCount(); }
     void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
 
     void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9d246ff..21c91a2 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -310,7 +310,7 @@
     for (uint32_t i = 0; i < surface->mImageCount; ++i) {
         GrVkImageInfo info;
         info.fImage = surface->mImages[i];
-        info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+        info.fAlloc = GrVkAlloc();
         info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
         info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
         info.fFormat = format;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 17a8d5c..5b627ec 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -53,6 +53,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.view.KeyEvent;
 
@@ -4784,6 +4785,28 @@
     }
 
     /**
+     * Convert {@link AudioDeviceInfo} to {@link MicrophoneInfo}.
+     * @hide
+     */
+    public static MicrophoneInfo microphoneInfoFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
+        int deviceType = deviceInfo.getType();
+        int micLocation = (deviceType == AudioDeviceInfo.TYPE_BUILTIN_MIC
+                || deviceType == AudioDeviceInfo.TYPE_TELEPHONY) ? MicrophoneInfo.LOCATION_MAINBODY
+                : deviceType == AudioDeviceInfo.TYPE_UNKNOWN ? MicrophoneInfo.LOCATION_UNKNOWN
+                        : MicrophoneInfo.LOCATION_PERIPHERAL;
+        MicrophoneInfo microphone = new MicrophoneInfo(
+                deviceInfo.getPort().name() + deviceInfo.getId(),
+                deviceInfo.getPort().type(), deviceInfo.getAddress(), micLocation,
+                MicrophoneInfo.GROUP_UNKNOWN, MicrophoneInfo.INDEX_IN_THE_GROUP_UNKNOWN,
+                MicrophoneInfo.POSITION_UNKNOWN, MicrophoneInfo.ORIENTATION_UNKNOWN,
+                new ArrayList<Pair<Float, Float>>(), new ArrayList<Pair<Integer, Integer>>(),
+                MicrophoneInfo.SENSITIVITY_UNKNOWN, MicrophoneInfo.SPL_UNKNOWN,
+                MicrophoneInfo.SPL_UNKNOWN, MicrophoneInfo.DIRECTIONALITY_UNKNOWN);
+        microphone.setId(deviceInfo.getId());
+        return microphone;
+    }
+
+    /**
      * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
      * of all available microphones. The list is empty when no microphones are available
      * on the device. An error during the query will result in an IOException being thrown.
@@ -4800,6 +4823,15 @@
             return new ArrayList<MicrophoneInfo>(); // Always return a list.
         }
         setPortIdForMicrophones(microphones);
+        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+        for (AudioDeviceInfo device : devices) {
+            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
+                    device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
+                continue;
+            }
+            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+            microphones.add(microphone);
+        }
         return microphones;
     }
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 3847530..4f0dccb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -41,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -1630,6 +1631,20 @@
             return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+        // Use routed device when there is not information returned by hal.
+        if (activeMicrophones.size() == 0) {
+            AudioDeviceInfo device = getRoutedDevice();
+            if (device != null) {
+                MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+                ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+                for (int i = 0; i < mChannelCount; i++) {
+                    channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+                }
+                microphone.setChannelMapping(channelMapping);
+                activeMicrophones.add(microphone);
+            }
+        }
         return activeMicrophones;
     }
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9ad5cd9..90b6bff 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -29,6 +29,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Surface;
 
 import java.io.File;
@@ -105,6 +106,8 @@
     private OnErrorListener mOnErrorListener;
     private OnInfoListener mOnInfoListener;
 
+    private int mChannelCount;
+
     /**
      * Default constructor.
      */
@@ -119,6 +122,7 @@
             mEventHandler = null;
         }
 
+        mChannelCount = 1;
         String packageName = ActivityThread.currentPackageName();
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -755,6 +759,7 @@
         if (numChannels <= 0) {
             throw new IllegalArgumentException("Number of channels is not positive");
         }
+        mChannelCount = numChannels;
         setParameter("audio-param-number-of-channels=" + numChannels);
     }
 
@@ -1432,6 +1437,20 @@
             return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+        // Use routed device when there is not information returned by hal.
+        if (activeMicrophones.size() == 0) {
+            AudioDeviceInfo device = getRoutedDevice();
+            if (device != null) {
+                MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+                ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+                for (int i = 0; i < mChannelCount; i++) {
+                    channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+                }
+                microphone.setChannelMapping(channelMapping);
+                activeMicrophones.add(microphone);
+            }
+        }
         return activeMicrophones;
     }
 
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 131e37b..004efea 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -92,6 +92,38 @@
      */
     public static final int CHANNEL_MAPPING_PROCESSED = 2;
 
+    /**
+     * Value used for when the group of the microphone is unknown.
+     */
+    public static final int GROUP_UNKNOWN = -1;
+
+    /**
+     * Value used for when the index in the group of the microphone is unknown.
+     */
+    public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1;
+
+    /**
+     * Value used for when the position of the microphone is unknown.
+     */
+    public static final Coordinate3F POSITION_UNKNOWN = new Coordinate3F(
+            -Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
+
+    /**
+     * Value used for when the orientation of the microphone is unknown.
+     */
+    public static final Coordinate3F ORIENTATION_UNKNOWN = new Coordinate3F(0.0f, 0.0f, 0.0f);
+
+    /**
+     * Value used for when the sensitivity of the microphone is unknown.
+     */
+    public static final float SENSITIVITY_UNKNOWN = -Float.MAX_VALUE;
+
+    /**
+     * Value used for when the SPL of the microphone is unknown. This value could be used when
+     * maximum SPL or minimum SPL is unknown.
+     */
+    public static final float SPL_UNKNOWN = -Float.MAX_VALUE;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "LOCATION_" }, value = {
             LOCATION_UNKNOWN,
@@ -216,7 +248,7 @@
      * Returns A device group id that can be used to group together microphones on the same
      * peripheral, attachments or logical groups. Main body is usually group 0.
      *
-     * @return the group of the microphone
+     * @return the group of the microphone or {@link #GROUP_UNKNOWN} if the group is unknown
      */
     public int getGroup() {
         return mGroup;
@@ -225,7 +257,8 @@
     /**
      * Returns unique index for device within its group.
      *
-     * @return the microphone's index in its group
+     * @return the microphone's index in its group or {@link #INDEX_IN_THE_GROUP_UNKNOWN} if the
+     * index in the group is unknown
      */
     public int getIndexInTheGroup() {
         return mIndexInTheGroup;
@@ -233,10 +266,11 @@
 
     /**
      * Returns A {@link Coordinate3F} object that represents the geometric location of microphone
-     * in meters, from botton-left-back corner of appliance. X-axis, Y-axis and Z-axis show
+     * in meters, from bottom-left-back corner of appliance. X-axis, Y-axis and Z-axis show
      * as the x, y, z values.
      *
-     * @return the geometric location of the microphone
+     * @return the geometric location of the microphone or {@link #POSITION_UNKNOWN} if the
+     * geometric location is unknown
      */
     public Coordinate3F getPosition() {
         return mPosition;
@@ -247,7 +281,8 @@
      * X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized
      * such as sqrt(x^2 + y^2 + z^2) equals 1.
      *
-     * @return the orientation of the microphone
+     * @return the orientation of the microphone or {@link #ORIENTATION_UNKNOWN} if orientation
+     * is unknown
      */
     public Coordinate3F getOrientation() {
         return mOrientation;
@@ -283,7 +318,8 @@
     /**
      * Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL.
      *
-     * @return the sensitivity of the microphone
+     * @return the sensitivity of the microphone or {@link #SENSITIVITY_UNKNOWN} if the sensitivity
+     * is unknown
      */
     public float getSensitivity() {
         return mSensitivity;
@@ -292,7 +328,7 @@
     /**
      * Returns the level in dB of the maximum SPL supported by the device at 1000Hz.
      *
-     * @return the maximum level in dB
+     * @return the maximum level in dB or {@link #SPL_UNKNOWN} if maximum SPL is unknown
      */
     public float getMaxSpl() {
         return mMaxSpl;
@@ -301,7 +337,7 @@
     /**
      * Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz.
      *
-     * @return the minimum level in dB
+     * @return the minimum level in dB or {@link #SPL_UNKNOWN} if minimum SPL is unknown
      */
     public float getMinSpl() {
         return mMinSpl;
@@ -327,8 +363,16 @@
         mPortId = portId;
     }
 
+    /**
+     * Set the channel mapping for the device.
+     * @hide
+     */
+    public void setChannelMapping(List<Pair<Integer, Integer>> channelMapping) {
+        mChannelMapping = channelMapping;
+    }
+
     /* A class containing three float value to represent a 3D coordinate */
-    public class Coordinate3F {
+    public static final class Coordinate3F {
         public final float x;
         public final float y;
         public final float z;
@@ -338,5 +382,17 @@
             this.y = y;
             this.z = z;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Coordinate3F)) {
+                return false;
+            }
+            Coordinate3F other = (Coordinate3F) obj;
+            return this.x == other.x && this.y == other.y && this.z == other.z;
+        }
     }
 }
diff --git a/packages/FusedLocation/res/values-as/strings.xml b/packages/FusedLocation/res/values-as/strings.xml
new file mode 100644
index 0000000..0d2cccc
--- /dev/null
+++ b/packages/FusedLocation/res/values-as/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="5379477904423203699">"Fused Location"</string>
+</resources>
diff --git a/packages/FusedLocation/res/values-or/strings.xml b/packages/FusedLocation/res/values-or/strings.xml
new file mode 100644
index 0000000..b95bc37a
--- /dev/null
+++ b/packages/FusedLocation/res/values-or/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="5379477904423203699">"ଫ୍ୟୁଜ୍‍ଡ୍‍ ଲୋକେଶନ୍‍"</string>
+</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 53e8813..eba5edb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -31,6 +31,7 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -58,11 +59,11 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.HandlerCaller;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.printspooler.R;
 import com.android.printspooler.util.ApprovedPrintServices;
 
@@ -116,8 +117,6 @@
 
     private IPrintSpoolerClient mClient;
 
-    private HandlerCaller mHandlerCaller;
-
     private PersistenceManager mPersistanceManager;
 
     private NotificationController mNotificationController;
@@ -134,8 +133,6 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mHandlerCaller = new HandlerCaller(this, getMainLooper(),
-                new HandlerCallerCallback(), false);
 
         mPersistanceManager = new PersistenceManager();
         mNotificationController = new NotificationController(PrintSpoolerService.this);
@@ -230,93 +227,73 @@
     }
 
     private void sendOnPrintJobQueued(PrintJobInfo printJob) {
-        Message message = mHandlerCaller.obtainMessageO(
-                HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
-        mHandlerCaller.executeOrSendMessage(message);
+        Message message = PooledLambda.obtainMessage(
+                PrintSpoolerService::onPrintJobQueued, this, printJob);
+        Handler.getMain().executeOrSendMessage(message);
     }
 
     private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
-        Message message = mHandlerCaller.obtainMessageO(
-                HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
-        mHandlerCaller.executeOrSendMessage(message);
+        Message message = PooledLambda.obtainMessage(
+                PrintSpoolerService::onAllPrintJobsForServiceHandled, this, service);
+        Handler.getMain().executeOrSendMessage(message);
     }
 
     private void sendOnAllPrintJobsHandled() {
-        Message message = mHandlerCaller.obtainMessage(
-                HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
-        mHandlerCaller.executeOrSendMessage(message);
+        Message message = PooledLambda.obtainMessage(
+                PrintSpoolerService::onAllPrintJobsHandled, this);
+        Handler.getMain().executeOrSendMessage(message);
     }
 
-    private final class HandlerCallerCallback implements HandlerCaller.Callback {
-        public static final int MSG_SET_CLIENT = 1;
-        public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
-        public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
-        public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
-        public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
-        public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
 
-        @Override
-        public void executeMessage(Message message) {
-            switch (message.what) {
-                case MSG_SET_CLIENT: {
-                    synchronized (mLock) {
-                        mClient = (IPrintSpoolerClient) message.obj;
-                        if (mClient != null) {
-                            Message msg = mHandlerCaller.obtainMessage(
-                                    HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
-                            mHandlerCaller.sendMessageDelayed(msg,
-                                    CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
-                        }
-                    }
-                } break;
+    private void onPrintJobStateChanged(PrintJobInfo printJob) {
+        if (mClient != null) {
+            try {
+                mClient.onPrintJobStateChanged(printJob);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error notify for print job state change.", re);
+            }
+        }
+    }
 
-                case MSG_ON_PRINT_JOB_QUEUED: {
-                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                    if (mClient != null) {
-                        try {
-                            mClient.onPrintJobQueued(printJob);
-                        } catch (RemoteException re) {
-                            Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
-                        }
-                    }
-                } break;
+    private void onAllPrintJobsHandled() {
+        if (mClient != null) {
+            try {
+                mClient.onAllPrintJobsHandled();
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+            }
+        }
+    }
 
-                case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
-                    ComponentName service = (ComponentName) message.obj;
-                    if (mClient != null) {
-                        try {
-                            mClient.onAllPrintJobsForServiceHandled(service);
-                        } catch (RemoteException re) {
-                            Slog.e(LOG_TAG, "Error notify for all print jobs per service"
-                                    + " handled.", re);
-                        }
-                    }
-                } break;
+    private void onAllPrintJobsForServiceHandled(ComponentName service) {
+        if (mClient != null) {
+            try {
+                mClient.onAllPrintJobsForServiceHandled(service);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error notify for all print jobs per service"
+                        + " handled.", re);
+            }
+        }
+    }
 
-                case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
-                    if (mClient != null) {
-                        try {
-                            mClient.onAllPrintJobsHandled();
-                        } catch (RemoteException re) {
-                            Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
-                        }
-                    }
-                } break;
+    private void onPrintJobQueued(PrintJobInfo printJob) {
+        if (mClient != null) {
+            try {
+                mClient.onPrintJobQueued(printJob);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+            }
+        }
+    }
 
-                case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
-                    checkAllPrintJobsHandled();
-                } break;
-
-                case MSG_ON_PRINT_JOB_STATE_CHANGED: {
-                    if (mClient != null) {
-                        PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                        try {
-                            mClient.onPrintJobStateChanged(printJob);
-                        } catch (RemoteException re) {
-                            Slog.e(LOG_TAG, "Error notify for print job state change.", re);
-                        }
-                    }
-                } break;
+    private void setClient(IPrintSpoolerClient client) {
+        synchronized (mLock) {
+            mClient = client;
+            if (mClient != null) {
+                Message msg = PooledLambda.obtainMessage(
+                        PrintSpoolerService::checkAllPrintJobsHandled, this);
+                Handler.getMain().sendMessageDelayed(msg,
+                        CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
             }
         }
     }
@@ -379,10 +356,9 @@
             addPrintJobLocked(printJob);
             setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
 
-            Message message = mHandlerCaller.obtainMessageO(
-                    HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
-                    printJob);
-            mHandlerCaller.executeOrSendMessage(message);
+            Message message = PooledLambda.obtainMessage(
+                    PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+            Handler.getMain().executeOrSendMessage(message);
         }
     }
 
@@ -546,10 +522,9 @@
      * @param printJob The updated print job.
      */
     private void notifyPrintJobUpdated(PrintJobInfo printJob) {
-        Message message = mHandlerCaller.obtainMessageO(
-                HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
-                printJob);
-        mHandlerCaller.executeOrSendMessage(message);
+        Message message = PooledLambda.obtainMessage(
+                PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+        Handler.getMain().executeOrSendMessage(message);
 
         mNotificationController.onUpdateNotifications(mPrintJobs);
     }
@@ -742,10 +717,9 @@
                 }
                 mNotificationController.onUpdateNotifications(mPrintJobs);
 
-                Message message = mHandlerCaller.obtainMessageO(
-                        HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
-                        printJob);
-                mHandlerCaller.executeOrSendMessage(message);
+                Message message = PooledLambda.obtainMessage(
+                        PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+                Handler.getMain().executeOrSendMessage(message);
             }
         }
     }
@@ -1472,9 +1446,9 @@
 
         @Override
         public void setClient(IPrintSpoolerClient client) {
-            Message message = mHandlerCaller.obtainMessageO(
-                    HandlerCallerCallback.MSG_SET_CLIENT, client);
-            mHandlerCaller.executeOrSendMessage(message);
+            Message message = PooledLambda.obtainMessage(
+                    PrintSpoolerService::setClient, PrintSpoolerService.this, client);
+            Handler.getMain().executeOrSendMessage(message);
         }
 
         @Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index b6a003d..1644546 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -16,7 +16,6 @@
 
 package com.android.printspooler.model;
 
-import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
@@ -39,6 +38,7 @@
 import android.print.PrintDocumentInfo;
 import android.util.Log;
 
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.printspooler.R;
 import com.android.printspooler.util.PageRangeUtils;
 
@@ -549,6 +549,9 @@
     }
 
     private static abstract class AsyncCommand implements Runnable {
+        /** Message indicated the desire to {@link #forceCancel} a command */
+        static final int MSG_FORCE_CANCEL = 0;
+
         private static final int STATE_PENDING = 0;
         private static final int STATE_RUNNING = 1;
         private static final int STATE_COMPLETED = 2;
@@ -574,7 +577,7 @@
 
         public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
                 CommandDoneCallback doneCallback) {
-            mHandler = new AsyncCommandHandler(looper);
+            mHandler = new Handler(looper);
             mAdapter = adapter;
             mDocument = document;
             mDoneCallback = doneCallback;
@@ -594,12 +597,12 @@
          */
         protected void removeForceCancel() {
             if (DEBUG) {
-                if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) {
+                if (mHandler.hasMessages(MSG_FORCE_CANCEL)) {
                     Log.i(LOG_TAG, "[FORCE CANCEL] Removed");
                 }
             }
 
-            mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL);
+            mHandler.removeMessages(MSG_FORCE_CANCEL);
         }
 
         /**
@@ -628,7 +631,8 @@
                         Log.i(LOG_TAG, "[FORCE CANCEL] queued");
                     }
                     mHandler.sendMessageDelayed(
-                            mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL),
+                            PooledLambda.obtainMessage(AsyncCommand::forceCancel, this)
+                                    .setWhat(MSG_FORCE_CANCEL),
                             FORCE_CANCEL_TIMEOUT);
                 }
 
@@ -698,34 +702,15 @@
             return mError;
         }
 
-        /**
-         * Handler for the async command.
-         */
-        private class AsyncCommandHandler extends Handler {
-            /** Message indicated the desire for to force cancel a command */
-            final static int MSG_FORCE_CANCEL = 0;
-
-            AsyncCommandHandler(@NonNull Looper looper) {
-                super(looper);
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch (msg.what) {
-                    case MSG_FORCE_CANCEL:
-                        if (isCanceling()) {
-                            if (DEBUG) {
-                                Log.i(LOG_TAG, "[FORCE CANCEL] executed");
-                            }
-                            failed("Command did not respond to cancellation in "
-                                    + FORCE_CANCEL_TIMEOUT + " ms");
-
-                            mDoneCallback.onDone();
-                        }
-                        break;
-                    default:
-                        // not reached;
+        private void forceCancel() {
+            if (isCanceling()) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "[FORCE CANCEL] executed");
                 }
+                failed("Command did not respond to cancellation in "
+                        + FORCE_CANCEL_TIMEOUT + " ms");
+
+                mDoneCallback.onDone();
             }
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
new file mode 100644
index 0000000..246ca47
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.widget.CandidateInfo;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+/**
+ * Data model representing an app in DefaultAppPicker UI.
+ */
+public class DefaultAppInfo extends CandidateInfo {
+
+    public final int userId;
+    public final ComponentName componentName;
+    public final PackageItemInfo packageItemInfo;
+    public final String summary;
+    protected final PackageManagerWrapper mPm;
+    private final Context mContext;
+
+    public DefaultAppInfo(Context context, PackageManagerWrapper pm, int uid, ComponentName cn) {
+        this(context, pm, uid, cn, null /* summary */, true /* enabled */);
+    }
+
+    public DefaultAppInfo(Context context, PackageManagerWrapper pm, PackageItemInfo info) {
+        this(context, pm, info, null /* summary */, true /* enabled */);
+    }
+
+    public DefaultAppInfo(Context context, PackageManagerWrapper pm, int uid, ComponentName cn,
+                          String summary, boolean enabled) {
+        super(enabled);
+        mContext = context;
+        mPm = pm;
+        packageItemInfo = null;
+        userId = uid;
+        componentName = cn;
+        this.summary = summary;
+    }
+
+    public DefaultAppInfo(Context context, PackageManagerWrapper pm, PackageItemInfo info,
+                          String summary, boolean enabled) {
+        super(enabled);
+        mContext = context;
+        mPm = pm;
+        userId = UserHandle.myUserId();
+        packageItemInfo = info;
+        componentName = null;
+        this.summary = summary;
+    }
+
+    @Override
+    public CharSequence loadLabel() {
+        if (componentName != null) {
+            try {
+                final ComponentInfo componentInfo = getComponentInfo();
+                if (componentInfo != null) {
+                    return componentInfo.loadLabel(mPm.getPackageManager());
+                } else {
+                    final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+                            componentName.getPackageName(), 0, userId);
+                    return appInfo.loadLabel(mPm.getPackageManager());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                return null;
+            }
+        } else if (packageItemInfo != null) {
+            return packageItemInfo.loadLabel(mPm.getPackageManager());
+        } else {
+            return null;
+        }
+
+    }
+
+    @Override
+    public Drawable loadIcon() {
+        final IconDrawableFactory factory = IconDrawableFactory.newInstance(mContext);
+        if (componentName != null) {
+            try {
+                final ComponentInfo componentInfo = getComponentInfo();
+                final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+                        componentName.getPackageName(), 0, userId);
+                if (componentInfo != null) {
+                    return factory.getBadgedIcon(componentInfo, appInfo, userId);
+                } else {
+                    return factory.getBadgedIcon(appInfo);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                return null;
+            }
+        }
+        if (packageItemInfo != null) {
+            try {
+                final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+                        packageItemInfo.packageName, 0, userId);
+                return factory.getBadgedIcon(packageItemInfo, appInfo, userId);
+            } catch (PackageManager.NameNotFoundException e) {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getKey() {
+        if (componentName != null) {
+            return componentName.flattenToString();
+        } else if (packageItemInfo != null) {
+            return packageItemInfo.packageName;
+        } else {
+            return null;
+        }
+    }
+
+    private ComponentInfo getComponentInfo() {
+        try {
+            ComponentInfo componentInfo = AppGlobals.getPackageManager().getActivityInfo(
+                    componentName, 0, userId);
+            if (componentInfo == null) {
+                componentInfo = AppGlobals.getPackageManager().getServiceInfo(
+                        componentName, 0, userId);
+            }
+            return componentInfo;
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 3cda9c9..784c714 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -444,6 +444,7 @@
 
     private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
                                              int bluetoothProfile) {
+        mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
         synchronized (mCallbacks) {
             for (BluetoothCallback callback : mCallbacks) {
                 callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e1ebbc4..f6ec6a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -27,6 +27,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.bluetooth.BluetoothAdapter;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.settingslib.R;
 
@@ -461,12 +462,12 @@
     }
 
     /**
-     * Set the device status as active or non-active per Bluetooth profile.
+     * Update the device status as active or non-active per Bluetooth profile.
      *
      * @param isActive true if the device is active
      * @param bluetoothProfile the Bluetooth profile
      */
-    public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+    public void onActiveDeviceChanged(boolean isActive, int bluetoothProfile) {
         boolean changed = false;
         switch (bluetoothProfile) {
         case BluetoothProfile.A2DP:
@@ -478,7 +479,7 @@
             mIsActiveDeviceHeadset = isActive;
             break;
         default:
-            Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+            Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
                     " isActive " + isActive);
             break;
         }
@@ -487,6 +488,26 @@
         }
     }
 
+    /**
+     * Get the device status as active or non-active per Bluetooth profile.
+     *
+     * @param bluetoothProfile the Bluetooth profile
+     * @return true if the device is active
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    public boolean isActiveDevice(int bluetoothProfile) {
+        switch (bluetoothProfile) {
+            case BluetoothProfile.A2DP:
+                return mIsActiveDeviceA2dp;
+            case BluetoothProfile.HEADSET:
+                return mIsActiveDeviceHeadset;
+            default:
+                Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
+                break;
+        }
+        return false;
+    }
+
     void setRssi(short rssi) {
         if (mRssi != rssi) {
             mRssi = rssi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index c3ff617..a8e0039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -24,6 +24,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -167,6 +168,15 @@
             }
         }
     }
+
+    public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                                   int bluetoothProfile) {
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            boolean isActive = Objects.equals(cachedDevice, activeDevice);
+            cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
+        }
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java b/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java
new file mode 100644
index 0000000..cdb4ffd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.widget;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Base class for defining a selectable item in UI.
+ */
+public abstract class CandidateInfo {
+
+    public final boolean enabled;
+
+    public CandidateInfo(boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public abstract CharSequence loadLabel();
+
+    public abstract Drawable loadIcon();
+
+    public abstract String getKey();
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
index b1f3f3c..235daf2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -130,6 +130,13 @@
     }
 
     /**
+     * Calls {@code PackageManager.queryIntentServices}
+     */
+    public List<ResolveInfo> queryIntentServices(Intent intent, int i) {
+        return mPm.queryIntentServices(intent, i);
+    }
+
+    /**
      * Calls {@code PackageManager.replacePreferredActivity}
      */
     public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
new file mode 100644
index 0000000..6a161d0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DefaultAppInfoTest {
+
+    @Mock
+    private PackageItemInfo mPackageItemInfo;
+    @Mock
+    private ComponentName mComponentName;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PackageManagerWrapper mPackageManagerWrapper;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private Drawable mIcon;
+
+    private Context mContext;
+    private DefaultAppInfo mInfo;
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(RuntimeEnvironment.application);
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        when(mPackageManagerWrapper.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManagerWrapper.getApplicationInfoAsUser(anyString(), anyInt(),
+                anyInt())).thenReturn(mApplicationInfo);
+        when(mPackageManager.loadUnbadgedItemIcon(mPackageItemInfo, mApplicationInfo)).thenReturn(
+                mIcon);
+    }
+
+    @Test
+    public void initInfoWithActivityInfo_shouldLoadInfo() {
+        mPackageItemInfo.packageName = "test";
+        mInfo = new DefaultAppInfo(mContext, mPackageManagerWrapper, mPackageItemInfo);
+        mInfo.loadLabel();
+        Drawable icon = mInfo.loadIcon();
+
+        assertThat(mInfo.getKey()).isEqualTo(mPackageItemInfo.packageName);
+        assertThat(icon).isNotNull();
+        verify(mPackageItemInfo).loadLabel(mPackageManager);
+    }
+
+    @Test
+    public void initInfoWithComponent_shouldLoadInfo() {
+        when(mComponentName.getPackageName()).thenReturn("com.android.settings");
+
+        mInfo = new DefaultAppInfo(mContext, mPackageManagerWrapper, 0 /* uid */, mComponentName);
+        mInfo.getKey();
+
+        verify(mComponentName).flattenToString();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
new file mode 100644
index 0000000..d6b2006
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Collection;
+
+@RunWith(RobolectricTestRunner.class)
+public class CachedBluetoothDeviceManagerTest {
+    private final static String DEVICE_NAME_1 = "TestName_1";
+    private final static String DEVICE_NAME_2 = "TestName_2";
+    private final static String DEVICE_ALIAS_1 = "TestAlias_1";
+    private final static String DEVICE_ALIAS_2 = "TestAlias_2";
+    private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+    private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+    private final BluetoothClass DEVICE_CLASS_1 =
+        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+    private final BluetoothClass DEVICE_CLASS_2 =
+        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+    @Mock
+    private LocalBluetoothAdapter mLocalAdapter;
+    @Mock
+    private LocalBluetoothProfileManager mLocalProfileManager;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private BluetoothEventManager mBluetoothEventManager;
+    @Mock
+    private HeadsetProfile mHfpProfile;
+    @Mock
+    private A2dpProfile mA2dpProfile;
+    @Mock
+    private PanProfile mPanProfile;
+    @Mock
+    private BluetoothDevice mDevice1;
+    @Mock
+    private BluetoothDevice mDevice2;
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    private Context mContext;
+    private String[] mActiveDeviceStringsArray;
+    private String mActiveDeviceStringNone;
+    private String mActiveDeviceStringAll;
+    private String mActiveDeviceStringMedia;
+    private String mActiveDeviceStringPhone;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+        when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+        when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
+        when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
+        when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
+        when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
+        when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
+        when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
+
+        when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+        when(mLocalAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+        when(mHfpProfile.isProfileReady()).thenReturn(true);
+        when(mA2dpProfile.isProfileReady()).thenReturn(true);
+        when(mPanProfile.isProfileReady()).thenReturn(true);
+        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
+    }
+
+    /**
+     * Test to verify addDevice().
+     */
+    @Test
+    public void testAddDevice_validCachedDevices_devicesAdded() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).contains(cachedDevice1);
+        assertThat(devices).contains(cachedDevice2);
+
+        assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
+        assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
+    }
+
+    /**
+     * Test to verify getName().
+     */
+    @Test
+    public void testGetName_validCachedDevice_nameFound() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
+    }
+
+    /**
+     * Test to verify onDeviceNameUpdated().
+     */
+    @Test
+    public void testOnDeviceNameUpdated_validName_nameUpdated() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
+
+        final String newAliasName = "NewAliasName";
+        when(mDevice1.getAliasName()).thenReturn(newAliasName);
+        mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
+        assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
+    }
+
+    /**
+     * Test to verify clearNonBondedDevices().
+     */
+    @Test
+    public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+        mCachedDeviceManager.clearNonBondedDevices();
+        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).contains(cachedDevice1);
+        assertThat(devices).contains(cachedDevice2);
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        mCachedDeviceManager.clearNonBondedDevices();
+        devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).contains(cachedDevice1);
+        assertThat(devices).doesNotContain(cachedDevice2);
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        mCachedDeviceManager.clearNonBondedDevices();
+        devices = mCachedDeviceManager.getCachedDevicesCopy();
+        assertThat(devices).doesNotContain(cachedDevice1);
+        assertThat(devices).doesNotContain(cachedDevice2);
+    }
+
+    /**
+     * Test to verify onBtClassChanged().
+     */
+    @Test
+    public void testOnBtClassChanged_validBtClass_classChanged() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
+
+        final BluetoothClass newBluetoothClass = DEVICE_CLASS_2;
+        when(mDevice1.getBluetoothClass()).thenReturn(newBluetoothClass);
+        mCachedDeviceManager.onBtClassChanged(mDevice1);
+        assertThat(cachedDevice1.getBtClass()).isEqualTo(newBluetoothClass);
+    }
+
+    /**
+     * Test to verify onDeviceDisappeared().
+     */
+    @Test
+    public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isFalse();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
+    }
+
+    /**
+     * Test to verify onActiveDeviceChanged().
+     */
+    @Test
+    public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        // Connect both devices for A2DP and HFP
+        cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+
+        // Verify that both devices are connected and none is Active
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+        // The first device is active for A2DP, the second device is active for HFP
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+        // The first device is active for A2DP and HFP
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+        // The second device is active for A2DP and HFP
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+        // No active device for A2DP
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+        // No active device for HFP
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 92c68e6..0775727 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -147,7 +147,7 @@
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for A2DP and test connection state summary
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
 
         // Test with battery level
@@ -163,7 +163,7 @@
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set A2DP profile to be connected, Active and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
 
         // Set A2DP profile to be disconnected and test connection state summary
@@ -179,7 +179,7 @@
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for HFP and test connection state summary
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
 
         // Test with battery level
@@ -195,7 +195,7 @@
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set HFP profile to be connected, Active and test connection state summary
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
 
         // Set HFP profile to be disconnected and test connection state summary
@@ -212,8 +212,8 @@
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for A2DP and HFP and test connection state summary
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
 
         // Test with battery level
@@ -222,16 +222,16 @@
                 "Connected, battery 10%, active");
 
         // Disconnect A2DP only and test connection state summary
-        mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "Connected (no media), battery 10%, active(phone)");
 
         // Disconnect HFP only and test connection state summary
-        mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "Connected (no phone), battery 10%, active(media)");
 
@@ -240,8 +240,8 @@
         // Set A2DP and HFP profiles to be connected, Active and test connection state summary
         mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
-        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
 
         // Set A2DP and HFP profiles to be disconnected and test connection state summary
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index dd89df1..2b181dc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -858,7 +858,10 @@
                 out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
                 out.writeInt(policies.length);
                 for (NetworkPolicy policy : policies) {
-                    if (policy != null) {
+                    // We purposefully only backup policies that the user has
+                    // defined; any inferred policies might include
+                    // carrier-protected data that we can't export.
+                    if (policy != null && !policy.inferred) {
                         byte[] marshaledPolicy = policy.getBytesForBackup();
                         out.writeByte(BackupUtils.NOT_NULL);
                         out.writeInt(marshaledPolicy.length);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 0fee81be..b000d84 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -448,6 +448,9 @@
                 Settings.Global.CARRIER_APP_WHITELIST,
                 GlobalSettingsProto.CARRIER_APP_WHITELIST);
         dumpSetting(s, p,
+                Settings.Global.CARRIER_APP_NAMES,
+                GlobalSettingsProto.CARRIER_APP_NAMES);
+        dumpSetting(s, p,
                 Settings.Global.USB_MASS_STORAGE_ENABLED,
                 GlobalSettingsProto.USB_MASS_STORAGE_ENABLED);
         dumpSetting(s, p,
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 713c573..fa14d1b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -62,7 +62,7 @@
                 android:layout_gravity="center_horizontal"
                 android:layout_centerHorizontal="true"
                 android:layout_alignParentTop="true"
-                android:letterSpacing="0.04"
+                android:letterSpacing="0.03"
                 android:textColor="?attr/wallpaperTextColor"
                 android:singleLine="true"
                 style="@style/widget_big_thin"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 4934e14..1c1c757 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -60,8 +60,8 @@
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
     <dimen name="widget_icon_padding">8dp</dimen>
-    <!-- Dash between notification shelf and date/alarm -->
-    <dimen name="widget_bottom_separator_padding">29dp</dimen>
+    <!-- Space between notification shelf and dash above it -->
+    <dimen name="widget_bottom_separator_padding">28dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
deleted file mode 100644
index f6dbc3d..0000000
--- a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<resources>
-    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
deleted file mode 100644
index b2231a6..0000000
--- a/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">4</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
deleted file mode 100644
index 905e9e3..0000000
--- a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<resources>
-    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h650dp/config.xml b/packages/SystemUI/res/values-h650dp/config.xml
deleted file mode 100644
index ee641b4..0000000
--- a/packages/SystemUI/res/values-h650dp/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">5</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-h650dp/dimens.xml b/packages/SystemUI/res/values-h650dp/dimens.xml
index 3811f67..8a00953 100644
--- a/packages/SystemUI/res/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res/values-h650dp/dimens.xml
@@ -16,7 +16,4 @@
 
 <resources>
     <dimen name="keyguard_clock_notifications_margin">32dp</dimen>
-
-    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">18.5%</fraction>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6594bd28..c4c4671 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,10 +15,6 @@
   ~ limitations under the License
   -->
 <resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-         card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
-
     <!-- Whether QuickSettings is in a phone landscape -->
     <bool name="quick_settings_wide">false</bool>
 
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 13ca114..f2df4b9 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -16,9 +16,6 @@
 */
 -->
 <resources>
-    <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
-
     <dimen name="keyguard_clock_notifications_margin">36dp</dimen>
 
     <dimen name="keyguard_indication_margin_bottom">80dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 63b9d75..67dabdb 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -26,10 +26,6 @@
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">1</integer>
 
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-         card. -->
-    <integer name="keyguard_max_notification_count">5</integer>
-
     <!-- Set to true to enable the user switcher on the keyguard. -->
     <bool name="config_keyguardUserSwitcher">true</bool>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index e95d9fe..923edc8 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -38,12 +38,6 @@
     <!-- On tablets this is just the close_handle_height -->
     <dimen name="peek_height">@dimen/close_handle_height</dimen>
 
-    <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
-         max value is used when no notifications are displaying, and the min value is when the
-         highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">34%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
     <dimen name="keyguard_clock_notifications_margin">44dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
deleted file mode 100644
index 1b50288..0000000
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<resources>
-    <integer name="keyguard_max_notification_count">4</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 511d45f..0000000
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<resources>
-    <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
-         min value is used when no notifications are displaying, and the max value is when the
-         highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">35%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 25e96c8..8cf4adb 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -28,11 +28,5 @@
     <dimen name="notification_panel_min_height">770dp</dimen>
     <!-- Bottom margin (from display edge) for status bar panels -->
     <dimen name="panel_float">56dp</dimen>
-
-    <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
-     max value is used when no notifications are displaying, and the min value is when the
-     highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">35%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">25%</fraction>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1ec59bf..aefcb55 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -439,15 +439,10 @@
     <!-- Minimum distance the user has to drag down to go to the full shade. -->
     <dimen name="keyguard_drag_down_min_distance">100dp</dimen>
 
-    <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
-         max value is used when no notifications are displaying, and the min value is when the
-         highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">45%</fraction>
-    <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
-
-    <!-- The margin between the clock and the notifications on Keyguard. See
-         keyguard_clock_height_fraction_* for the difference between min and max.-->
+    <!-- The margin between the clock and the notifications on Keyguard.-->
     <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
+    <!-- Minimum margin between clock and top of screen or ambient indication -->
+    <dimen name="keyguard_clock_top_margin">26dp</dimen>
     <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
@@ -885,9 +880,6 @@
             burn-in on AOD -->
     <dimen name="burn_in_prevention_offset_y">50dp</dimen>
 
-    <!-- padding between the notification stack and the keyguard status view when dozing -->
-    <dimen name="dozing_stack_padding">30dp</dimen>
-
     <dimen name="corner_size">16dp</dimen>
     <dimen name="top_padding">0dp</dimen>
     <dimen name="bottom_padding">48dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
index ddd27b0..a0e7752 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
@@ -17,15 +17,12 @@
 package com.android.systemui.shared.recents.model;
 
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
 
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
 
 /**
  * Background task resource loader
@@ -40,8 +37,7 @@
     private final Handler mMainThreadHandler;
 
     private final TaskResourceLoadQueue mLoadQueue;
-    private final TaskKeyLruCache<Drawable> mIconCache;
-    private final BitmapDrawable mDefaultIcon;
+    private final IconLoader mIconLoader;
 
     private boolean mStarted;
     private boolean mCancelled;
@@ -51,11 +47,9 @@
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
-            TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon,
-            OnIdleChangedListener onIdleChangedListener) {
+            IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
         mLoadQueue = loadQueue;
-        mIconCache = iconCache;
-        mDefaultIcon = defaultIcon;
+        mIconLoader = iconLoader;
         mMainThreadHandler = new Handler();
         mOnIdleChangedListener = onIdleChangedListener;
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
@@ -140,32 +134,7 @@
         // Load the next item from the queue
         final Task t = mLoadQueue.nextTask();
         if (t != null) {
-            Drawable cachedIcon = mIconCache.get(t.key);
-
-            // Load the icon if it is stale or we haven't cached one yet
-            if (cachedIcon == null) {
-                cachedIcon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(
-                        mContext, t.taskDescription, t.key.userId, mContext.getResources());
-
-                if (cachedIcon == null) {
-                    ActivityInfo info = PackageManagerWrapper.getInstance().getActivityInfo(
-                            t.key.getComponent(), t.key.userId);
-                    if (info != null) {
-                        if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
-                        cachedIcon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(
-                                info, t.key.userId);
-                    }
-                }
-
-                if (cachedIcon == null) {
-                    cachedIcon = mDefaultIcon;
-                }
-
-                // At this point, even if we can't load the icon, we will set the
-                // default icon.
-                mIconCache.put(t.key, cachedIcon);
-            }
-
+            final Drawable icon = mIconLoader.getIcon(t);
             if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
             final ThumbnailData thumbnailData =
                     ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
@@ -173,9 +142,8 @@
 
             if (!mCancelled) {
                 // Notify that the task data has changed
-                final Drawable finalIcon = cachedIcon;
                 mMainThreadHandler.post(
-                        () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon));
+                        () -> t.notifyTaskDataLoaded(thumbnailData, icon));
             }
         }
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
new file mode 100644
index 0000000..3bc1d9a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.shared.recents.model;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+public abstract class IconLoader {
+
+    private static final String TAG = "IconLoader";
+
+    protected final Context mContext;
+    protected final TaskKeyLruCache<Drawable> mIconCache;
+    protected final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+
+    public IconLoader(Context context, TaskKeyLruCache<Drawable> iconCache, LruCache<ComponentName,
+            ActivityInfo> activityInfoCache) {
+        mContext = context;
+        mIconCache = iconCache;
+        mActivityInfoCache = activityInfoCache;
+    }
+
+    /**
+     * Returns the activity info for the given task key, retrieving one from the system if the
+     * task key is expired.
+     *
+     * TODO: Move this to an ActivityInfoCache class
+     */
+    public ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+        ComponentName cn = taskKey.getComponent();
+        ActivityInfo activityInfo = mActivityInfoCache.get(cn);
+        if (activityInfo == null) {
+            activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId);
+            if (cn == null || activityInfo == null) {
+                Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
+                        activityInfo);
+                return null;
+            }
+            mActivityInfoCache.put(cn, activityInfo);
+        }
+        return activityInfo;
+    }
+
+    public Drawable getIcon(Task t) {
+        Drawable cachedIcon = mIconCache.get(t.key);
+        if (cachedIcon == null) {
+            cachedIcon = createNewIconForTask(t.key, t.taskDescription, true /* returnDefault */);
+            mIconCache.put(t.key, cachedIcon);
+        }
+        return cachedIcon;
+    }
+
+    /**
+     * Returns the cached task icon if the task key is not expired, updating the cache if it is.
+     */
+    public Drawable getAndInvalidateIfModified(Task.TaskKey taskKey,
+            ActivityManager.TaskDescription td, boolean loadIfNotCached) {
+        // Return the cached activity icon if it exists
+        Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
+        if (icon != null) {
+            return icon;
+        }
+
+        if (loadIfNotCached) {
+            icon = createNewIconForTask(taskKey, td, false /* returnDefault */);
+            if (icon != null) {
+                mIconCache.put(taskKey, icon);
+                return icon;
+            }
+        }
+
+        // We couldn't load any icon
+        return null;
+    }
+
+    private Drawable createNewIconForTask(Task.TaskKey taskKey,
+            ActivityManager.TaskDescription desc, boolean returnDefault) {
+        int userId = taskKey.userId;
+        Bitmap tdIcon = desc.getInMemoryIcon();
+        if (tdIcon != null) {
+            return createDrawableFromBitmap(tdIcon, userId);
+        }
+        if (desc.getIconResource() != 0) {
+            // TODO: Use task context here
+            try {
+                return createBadgedDrawable(mContext.getDrawable(desc.getIconResource()), userId);
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Could not find icon drawable from resource", e);
+            }
+        }
+
+        tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+                desc.getIconFilename(), userId);
+        if (tdIcon != null) {
+            return createDrawableFromBitmap(tdIcon, userId);
+        }
+
+        // Load the icon from the activity info and cache it
+        ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
+        if (activityInfo != null) {
+            Drawable icon = getBadgedActivityIcon(activityInfo, userId);
+            if (icon != null) {
+                return icon;
+            }
+        }
+
+        // At this point, even if we can't load the icon, we will set the default icon.
+        return returnDefault ? getDefaultIcon(userId) : null;
+    }
+
+    public abstract Drawable getDefaultIcon(int userId);
+
+    protected Drawable createDrawableFromBitmap(Bitmap icon, int userId) {
+        return createBadgedDrawable(new BitmapDrawable(mContext.getResources(), icon), userId);
+    }
+
+    protected abstract Drawable createBadgedDrawable(Drawable icon, int userId);
+
+    /**
+     * @return the activity icon for the ActivityInfo for a user, badging if necessary.
+     */
+    protected abstract Drawable getBadgedActivityIcon(ActivityInfo info, int userId);
+
+    public static class DefaultIconLoader extends IconLoader {
+
+        private final BitmapDrawable mDefaultIcon;
+        private final IconDrawableFactory mDrawableFactory;
+
+        public DefaultIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
+                LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+            super(context, iconCache, activityInfoCache);
+
+            // Create the default assets
+            Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+            icon.eraseColor(0);
+            mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
+            mDrawableFactory = IconDrawableFactory.newInstance(context);
+        }
+
+        @Override
+        public Drawable getDefaultIcon(int userId) {
+            return mDefaultIcon;
+        }
+
+        @Override
+        protected Drawable createBadgedDrawable(Drawable icon, int userId) {
+            if (userId != UserHandle.myUserId()) {
+                icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(userId));
+            }
+            return icon;
+        }
+
+        @Override
+        protected Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
+            return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
+        }
+    }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index 4834bb1..76204df 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -123,7 +123,7 @@
                     ? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
                     : "";
             Drawable icon = isStackTask
-                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
+                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
                     : null;
             ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                     false /* loadIfNotCached */, false /* storeInCache */);
@@ -179,7 +179,7 @@
 
             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
                 if (task.icon == null) {
-                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
                             true);
                 }
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 0f68026..1309a60 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -21,9 +21,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.os.Trace;
@@ -36,7 +33,6 @@
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.Map;
@@ -61,8 +57,6 @@
     // Disable all thumbnail loading.
     public static final int SVELTE_DISABLE_LOADING = 3;
 
-    private final Context mContext;
-
     // This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
     // for many tasks, which we use to get the activity labels and icons.  Unlike the other caches
     // below, this is per-package so we can't invalidate the items in the cache based on the last
@@ -73,6 +67,7 @@
     private final TaskKeyLruCache<String> mActivityLabelCache;
     private final TaskKeyLruCache<String> mContentDescriptionCache;
     private final TaskResourceLoadQueue mLoadQueue;
+    private final IconLoader mIconLoader;
     private final BackgroundTaskLoader mLoader;
     private final HighResThumbnailLoader mHighResThumbnailLoader;
     @GuardedBy("this")
@@ -86,7 +81,6 @@
 
     private int mDefaultTaskBarBackgroundColor;
     private int mDefaultTaskViewBackgroundColor;
-    private final BitmapDrawable mDefaultIcon;
 
     private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
         @Override
@@ -99,16 +93,10 @@
 
     public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
             int svelteLevel) {
-        mContext = context;
         mMaxThumbnailCacheSize = maxThumbnailCacheSize;
         mMaxIconCacheSize = maxIconCacheSize;
         mSvelteLevel = svelteLevel;
 
-        // Create the default assets
-        Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
-        icon.eraseColor(0);
-        mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
-
         // Initialize the proxy, cache and loaders
         int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
         mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
@@ -119,10 +107,17 @@
         mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
                 mClearActivityInfoOnEviction);
         mActivityInfoCache = new LruCache<>(numRecentTasks);
-        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mDefaultIcon,
+
+        mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
+        mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader,
                 mHighResThumbnailLoader::setTaskLoadQueueIdle);
     }
 
+    protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
+            LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+        return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
+    }
+
     /**
      * Sets the default task bar/view colors if none are provided by the app.
      */
@@ -187,7 +182,7 @@
      */
     public void loadTaskData(Task t) {
         Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
-        icon = icon != null ? icon : mDefaultIcon;
+        icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
         mLoadQueue.addTask(t);
         t.notifyTaskDataLoaded(t.thumbnail, icon);
     }
@@ -195,7 +190,7 @@
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
         mLoadQueue.removeTask(t);
-        t.notifyTaskDataUnloaded(mDefaultIcon);
+        t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
     }
 
     /** Completely removes the resource data from the pool. */
@@ -205,7 +200,7 @@
         mActivityLabelCache.remove(t.key);
         mContentDescriptionCache.remove(t.key);
         if (notifyTaskDataUnloaded) {
-            t.notifyTaskDataUnloaded(mDefaultIcon);
+            t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
         }
     }
 
@@ -326,35 +321,8 @@
      * Returns the cached task icon if the task key is not expired, updating the cache if it is.
      */
     Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
-            Resources res, boolean loadIfNotCached) {
-        // Return the cached activity icon if it exists
-        Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
-        if (icon != null) {
-            return icon;
-        }
-
-        if (loadIfNotCached) {
-            // Return and cache the task description icon if it exists
-            icon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(mContext, td,
-                    taskKey.userId, res);
-            if (icon != null) {
-                mIconCache.put(taskKey, icon);
-                return icon;
-            }
-
-            // Load the icon from the activity info and cache it
-            ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
-            if (activityInfo != null) {
-                icon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(activityInfo,
-                        taskKey.userId);
-                if (icon != null) {
-                    mIconCache.put(taskKey, icon);
-                    return icon;
-                }
-            }
-        }
-        // We couldn't load any icon
-        return null;
+            boolean loadIfNotCached) {
+        return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
     }
 
     /**
@@ -417,18 +385,7 @@
      * task key is expired.
      */
     ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
-        ComponentName cn = taskKey.getComponent();
-        ActivityInfo activityInfo = mActivityInfoCache.get(cn);
-        if (activityInfo == null) {
-            activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId);
-            if (cn == null || activityInfo == null) {
-                Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
-                        activityInfo);
-                return null;
-            }
-            mActivityInfoCache.put(cn, activityInfo);
-        }
-        return activityInfo;
+        return mIconLoader.getAndUpdateActivityInfo(taskKey);
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 138910c..0103cad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -17,7 +17,6 @@
 package com.android.systemui.shared.system;
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -40,25 +39,19 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 
 import android.view.RemoteAnimationTarget;
-import android.view.WindowManagerGlobal;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -74,14 +67,12 @@
     private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
 
     private final PackageManager mPackageManager;
-    private final IconDrawableFactory mDrawableFactory;
     private final BackgroundExecutor mBackgroundExecutor;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
 
     private ActivityManagerWrapper() {
         final Context context = AppGlobals.getInitialApplication();
         mPackageManager = context.getPackageManager();
-        mDrawableFactory = IconDrawableFactory.newInstance(context);
         mBackgroundExecutor = BackgroundExecutor.get();
         mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
     }
@@ -156,58 +147,6 @@
     }
 
     /**
-     * @return the task description icon, loading and badging it if it necessary.
-     */
-    public Drawable getBadgedTaskDescriptionIcon(Context context,
-            ActivityManager.TaskDescription taskDescription, int userId, Resources res) {
-        Bitmap tdIcon = taskDescription.getInMemoryIcon();
-        Drawable dIcon = null;
-        if (tdIcon != null) {
-            dIcon = new BitmapDrawable(res, tdIcon);
-        } else if (taskDescription.getIconResource() != 0) {
-            try {
-                dIcon = context.getDrawable(taskDescription.getIconResource());
-            } catch (NotFoundException e) {
-                Log.e(TAG, "Could not find icon drawable from resource", e);
-            }
-        } else {
-            tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
-                    taskDescription.getIconFilename(), userId);
-            if (tdIcon != null) {
-                dIcon = new BitmapDrawable(res, tdIcon);
-            }
-        }
-        if (dIcon != null) {
-            return getBadgedIcon(dIcon, userId);
-        }
-        return null;
-    }
-
-    /**
-     * @return the given icon for a user, badging if necessary.
-     */
-    private Drawable getBadgedIcon(Drawable icon, int userId) {
-        if (userId != UserHandle.myUserId()) {
-            icon = mPackageManager.getUserBadgedIcon(icon, new UserHandle(userId));
-        }
-        return icon;
-    }
-
-    /**
-     * @return the activity icon for the ActivityInfo for a user, badging if necessary.
-     */
-    public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
-        return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
-    }
-
-    /**
-     * @return the application icon for the ApplicationInfo for a user, badging if necessary.
-     */
-    public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
-        return mDrawableFactory.getBadgedIcon(appInfo, userId);
-    }
-
-    /**
      * @return the activity label, badging if necessary.
      */
     public String getBadgedActivityLabel(ActivityInfo info, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0272a90..842899b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -22,6 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.Nullable;
+import android.app.AppGlobals;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -37,6 +38,7 @@
 import android.os.CountDownTimer;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -65,6 +67,8 @@
 public class TaskViewHeader extends FrameLayout
         implements View.OnClickListener, View.OnLongClickListener {
 
+    private static IconDrawableFactory sDrawableFactory;
+
     private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
     private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
     private static final int OVERLAY_REVEAL_DURATION = 250;
@@ -628,7 +632,7 @@
                 activityInfo.applicationInfo, userId));
         mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
                 mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
-        mAppIconView.setImageDrawable(ActivityManagerWrapper.getInstance().getBadgedApplicationIcon(
+        mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
                 activityInfo.applicationInfo, userId));
         mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
                 ? mLightInfoIcon
@@ -671,4 +675,11 @@
             revealAnim.start();
         }
     }
+
+    private static IconDrawableFactory getIconDrawableFactory() {
+        if (sDrawableFactory == null) {
+            sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
+        }
+        return sDrawableFactory;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 389be1a..0cf26df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -19,11 +19,10 @@
 import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
 
 import android.content.res.Resources;
-import android.graphics.Path;
 import android.util.MathUtils;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.PathInterpolator;
+import com.android.keyguard.KeyguardStatusView;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 /**
@@ -31,54 +30,83 @@
  */
 public class KeyguardClockPositionAlgorithm {
 
-    private static final float SLOW_DOWN_FACTOR = 0.4f;
-
-    private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
-    private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
-    private static final float CLOCK_SCALE_FADE_START = 0.95f;
-    private static final float CLOCK_SCALE_FADE_END = 0.75f;
-    private static final float CLOCK_SCALE_FADE_END_NO_NOTIFS = 0.5f;
-
-    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
-    private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
-
     private static final long MILLIS_PER_MINUTES = 1000 * 60;
     private static final float BURN_IN_PREVENTION_PERIOD_Y = 521;
     private static final float BURN_IN_PREVENTION_PERIOD_X = 83;
 
+    /**
+     * How much the clock height influences the shade position.
+     * 0 means nothing, 1 means move the shade up by the height of the clock
+     * 0.5f means move the shade up by half of the size of the clock.
+     */
+    private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
+
+    /**
+     * Margin between the bottom of the clock and the notification shade.
+     */
     private int mClockNotificationsMargin;
-    private float mClockYFractionMin;
-    private float mClockYFractionMax;
-    private int mMaxKeyguardNotifications;
-    private int mMaxPanelHeight;
+
+    /**
+     * Current height of {@link NotificationPanelView}, considering how much the
+     * user collapsed it.
+     */
     private float mExpandedHeight;
-    private int mNotificationCount;
+
+    /**
+     * Height of the parent view - display size in px.
+     */
     private int mHeight;
+
+    /**
+     * Height of {@link KeyguardStatusView}.
+     */
     private int mKeyguardStatusHeight;
-    private float mEmptyDragAmount;
-    private float mDensity;
+
+    /**
+     * Height of notification stack: Sum of height of each notification.
+     */
+    private int mNotificationStackHeight;
+
+    /**
+     * Minimum top margin to avoid overlap with status bar.
+     */
+    private int mMinTopMargin;
+
+    /**
+     * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
+     * the ambient indication.
+     */
+    private int mMaxShadeBottom;
+
+    /**
+     * Margin that we should respect within the available space.
+     */
+    private int mContainerPadding;
+
+    /**
+     * Position where clock should be when the panel is collapsed.
+     */
+    private int mClockYTarget;
+
+    /**
+     * @see NotificationPanelView#getMaxPanelHeight()
+     */
+    private float mMaxPanelHeight;
+
+    /**
+     * Burn-in prevention x translation.
+     */
     private int mBurnInPreventionOffsetX;
+
+    /**
+     * Burn-in prevention y translation.
+     */
     private int mBurnInPreventionOffsetY;
 
     /**
-     * The number (fractional) of notifications the "more" card counts when calculating how many
-     * notifications are currently visible for the y positioning of the clock.
+     * Doze/AOD transition amount.
      */
-    private float mMoreCardNotificationAmount;
-
-    private static final PathInterpolator sSlowDownInterpolator;
-
-    static {
-        Path path = new Path();
-        path.moveTo(0, 0);
-        path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
-        sSlowDownInterpolator = new PathInterpolator(path);
-    }
-
-    private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
-    private int mClockBottom;
     private float mDarkAmount;
-    private int mDozingStackPadding;
 
     /**
      * Refreshes the dimension values.
@@ -86,79 +114,95 @@
     public void loadDimens(Resources res) {
         mClockNotificationsMargin = res.getDimensionPixelSize(
                 R.dimen.keyguard_clock_notifications_margin);
-        mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
-        mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
-        mMoreCardNotificationAmount =
-                (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) /
-                        res.getDimensionPixelSize(R.dimen.notification_min_height);
-        mDensity = res.getDisplayMetrics().density;
+        mContainerPadding = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_top_margin);
         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y);
-        mDozingStackPadding = res.getDimensionPixelSize(R.dimen.dozing_stack_padding);
     }
 
-    public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
-            int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
-            int clockBottom, float dark) {
-        mMaxKeyguardNotifications = maxKeyguardNotifications;
-        mMaxPanelHeight = maxPanelHeight;
+    public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
+            float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
+            float dark) {
+        mMinTopMargin = minTopMargin;
+        mMaxShadeBottom = maxShadeBottom;
+        mNotificationStackHeight = notificationStackHeight;
         mExpandedHeight = expandedHeight;
-        mNotificationCount = notificationCount;
-        mHeight = height;
+        mMaxPanelHeight = maxPanelHeight;
+        mHeight = parentHeight;
         mKeyguardStatusHeight = keyguardStatusHeight;
-        mEmptyDragAmount = emptyDragAmount;
-        mClockBottom = clockBottom;
         mDarkAmount = dark;
-    }
 
-    public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
-        return mClockYFractionMin * height + keyguardStatusHeight / 2
-                + mClockNotificationsMargin;
+        // Where the clock should stop when swiping up.
+        // This should be outside of the display when unlocked or
+        // under then status bar when the bouncer will be shown
+        mClockYTarget = -mKeyguardStatusHeight;
+        // TODO: on bouncer animation follow-up CL
+        // mClockYTarget = mMinTopMargin + mContainerPadding;
     }
 
     public void run(Result result) {
-        int y = getClockY() - mKeyguardStatusHeight / 2;
-        float clockAdjustment = getClockYExpansionAdjustment();
-        float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
-        result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+        final int y = getClockY();
         result.clockY = y;
-        int clockNotificationsPadding = mClockNotificationsMargin
-                + result.stackScrollerPaddingAdjustment;
-        int padding = y + clockNotificationsPadding;
-        result.clockScale = getClockScale(mKeyguardStatusHeight + padding,
-                y, y + mClockNotificationsMargin + mKeyguardStatusHeight);
-        result.clockAlpha = getClockAlpha(result.clockScale);
-
-        result.stackScrollerPadding = mClockBottom + y + mDozingStackPadding;
+        result.clockAlpha = getClockAlpha(y);
+        result.stackScrollerPadding = y + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
-    private float getClockScale(int notificationPadding, int clockY, int startPadding) {
-        float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
-        float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
-        float distanceToScaleEnd = notificationPadding - scaleEnd;
-        float progress = distanceToScaleEnd / (startPadding - scaleEnd);
-        progress = Math.max(0.0f, Math.min(progress, 1.0f));
-        progress = mAccelerateInterpolator.getInterpolation(progress);
-        progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f);
-        return interpolate(progress, 1, mDarkAmount);
+    public float getMinStackScrollerPadding() {
+        return mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
     }
 
-    private float getClockYFraction() {
-        float t = getNotificationAmountT();
-        t = Math.min(t, 1.0f);
-        return MathUtils.lerp(mClockYFractionMax, mClockYFractionMin, t);
+    private int getMaxClockY() {
+        return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
+    }
+
+    public int getExpandedClockBottom() {
+        return getExpandedClockPosition() + mKeyguardStatusHeight;
+    }
+
+    /**
+     * Vertically align the clock and the shade in the available space considering only
+     * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
+     * @return Clock Y in pixels.
+     */
+    private int getExpandedClockPosition() {
+        final int availableHeight = mMaxShadeBottom - mMinTopMargin;
+        final int containerCenter = mMinTopMargin + availableHeight / 2;
+
+        float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT
+                - mClockNotificationsMargin - mNotificationStackHeight / 2;
+        if (y < mMinTopMargin + mContainerPadding) {
+            y = mMinTopMargin + mContainerPadding;
+        }
+
+        // Don't allow the clock base to be under half of the screen
+        final float maxClockY = getMaxClockY();
+        if (y > maxClockY) {
+            y = maxClockY;
+        }
+
+        return (int) y;
     }
 
     private int getClockY() {
         // Dark: Align the bottom edge of the clock at about half of the screen:
-        float clockYDark = (mClockYFractionMax * mHeight +
-                (float) mKeyguardStatusHeight / 2 - mClockBottom)
-                + burnInPreventionOffsetY();
-        float clockYRegular = getClockYFraction() * mHeight;
-        return (int) interpolate(clockYRegular, clockYDark, mDarkAmount);
+        final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
+        float clockYRegular = getExpandedClockPosition();
+
+        // Move clock up while collapsing the shade
+        final float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
+        final float clockY = MathUtils.lerp(mClockYTarget, clockYRegular, shadeExpansion);
+
+        return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
+    }
+
+    private float getClockAlpha(int y) {
+        float alphaKeyguard = Math.max(0, Math.min(1, (y - mMinTopMargin)
+                / Math.max(1f, getExpandedClockPosition() - mMinTopMargin)));
+        alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
+        return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
     }
 
     private float burnInPreventionOffsetY() {
@@ -190,63 +234,19 @@
         return interpolate(0, amplitude, interpolationAmount);
     }
 
-    private float getClockYExpansionAdjustment() {
-        float rubberbandFactor = getClockYExpansionRubberbandFactor();
-        float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
-        float t = value / mMaxPanelHeight;
-        float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
-                * mMaxPanelHeight;
-        if (mNotificationCount == 0) {
-            return (-2*value + slowedDownValue)/3;
-        } else {
-            return slowedDownValue;
-        }
-    }
-
-    private float getClockYExpansionRubberbandFactor() {
-        float t = getNotificationAmountT();
-        t = Math.min(t, 1.0f);
-        t = (float) Math.pow(t, 0.3f);
-        return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
-    }
-
-    private float getTopPaddingAdjMultiplier() {
-        float t = getNotificationAmountT();
-        t = Math.min(t, 1.0f);
-        return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
-                + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
-    }
-
-    private float getClockAlpha(float scale) {
-        float fadeEnd = getNotificationAmountT() == 0.0f
-                ? CLOCK_SCALE_FADE_END_NO_NOTIFS
-                : CLOCK_SCALE_FADE_END;
-        float alpha = (scale - fadeEnd)
-                / (CLOCK_SCALE_FADE_START - fadeEnd);
-        return Math.max(0, Math.min(1, alpha));
-    }
-
-    /**
-     * @return a value from 0 to 1 depending on how many notification there are
-     */
-    private float getNotificationAmountT() {
-        return mNotificationCount
-                / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
-    }
-
     public static class Result {
 
         /**
+         * The x translation of the clock.
+         */
+        public int clockX;
+
+        /**
          * The y translation of the clock.
          */
         public int clockY;
 
         /**
-         * The scale of the Clock
-         */
-        public float clockScale;
-
-        /**
          * The alpha value of the clock.
          */
         public float clockAlpha;
@@ -255,14 +255,5 @@
          * The top padding of the stack scroller, in pixels.
          */
         public int stackScrollerPadding;
-
-        /**
-         * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
-         * the padding, but not the overall panel size.
-         */
-        public int stackScrollerPaddingAdjustment;
-
-        /** The x translation of the clock. */
-        public int clockX;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 3b129fc..51d094e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -37,6 +37,7 @@
 import android.os.PowerManager;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.Log;
 import android.util.MathUtils;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -172,7 +173,6 @@
     private Animator mClockAnimator;
     private int mClockAnimationTargetX = Integer.MIN_VALUE;
     private int mClockAnimationTargetY = Integer.MIN_VALUE;
-    private int mTopPaddingAdjustment;
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
@@ -244,6 +244,7 @@
     private int mQsNotificationTopPadding;
     private float mExpandOffset;
     private boolean mHideIconsDuringNotificationLaunch = true;
+    private int mStackScrollerMeasuringPass;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -459,17 +460,17 @@
         if (mStatusBarState != StatusBarState.KEYGUARD) {
             stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
             +  mQsNotificationTopPadding;
-            mTopPaddingAdjustment = 0;
         } else {
+            final int totalHeight = getHeight();
+            final int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
             mClockPositionAlgorithm.setup(
-                    mStatusBar.getMaxNotificationsWhileLocked(),
-                    getMaxPanelHeight(),
+                    mStatusBarMinHeight,
+                    totalHeight - bottomPadding,
+                    calculatePanelHeightShade() - mNotificationStackScroller.getTopPadding(),
                     getExpandedHeight(),
-                    mNotificationStackScroller.getNotGoneChildCount(),
-                    getHeight(),
+                    getMaxPanelHeight(),
+                    totalHeight,
                     mKeyguardStatusView.getHeight(),
-                    mEmptyDragAmount,
-                    mKeyguardStatusView.getClockBottom(),
                     mDarkAmount);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
@@ -478,14 +479,16 @@
                 mKeyguardStatusView.setX(mClockPositionResult.clockX);
                 mKeyguardStatusView.setY(mClockPositionResult.clockY);
             }
-            updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
+            updateClock();
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
-            mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
         mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
         mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
+
+        mStackScrollerMeasuringPass++;
         requestScrollerTopPaddingUpdate(animate);
+        mStackScrollerMeasuringPass = 0;
     }
 
     /**
@@ -493,8 +496,7 @@
      * @return the maximum keyguard notifications that can fit on the screen
      */
     public int computeMaxKeyguardNotifications(int maximum) {
-        float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(),
-                mKeyguardStatusView.getHeight());
+        float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
         int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
                 R.dimen.notification_divider_height));
         NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
@@ -579,12 +581,10 @@
         });
     }
 
-    private void updateClock(float alpha, float scale) {
+    private void updateClock() {
         if (!mKeyguardStatusViewAnimating) {
-            mKeyguardStatusView.setAlpha(alpha * mQsClockAlphaOverride);
+            mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha * mQsClockAlphaOverride);
         }
-        mKeyguardStatusView.setScaleX(scale);
-        mKeyguardStatusView.setScaleY(scale);
     }
 
     public void animateToFullShade(long delay) {
@@ -1316,7 +1316,7 @@
         newClockAlpha = 1 - MathUtils.constrain(newClockAlpha, 0, 1);
         if (newClockAlpha != mQsClockAlphaOverride) {
             mQsClockAlphaOverride = Interpolators.ALPHA_OUT.getInterpolation(newClockAlpha);
-            updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
+            updateClock();
         }
 
         if (mAccessibilityManager.isEnabled()) {
@@ -1356,15 +1356,15 @@
                 && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
 
             // Either QS pushes the notifications down when fully expanded, or QS is fully above the
-            // notifications (mostly on tablets). maxNotifications denotes the normal top padding
-            // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to
-            // take the maximum and linearly interpolate with the panel expansion for a nice motion.
-            int maxNotifications = mClockPositionResult.stackScrollerPadding
-                    - mClockPositionResult.stackScrollerPaddingAdjustment;
-            int maxQs = mQsMaxExpansionHeight + mQsNotificationTopPadding;
+            // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
+            // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
+            // panel. We need to take the maximum and linearly interpolate with the panel expansion
+            // for a nice motion.
+            int maxNotificationPadding = mClockPositionResult.stackScrollerPadding;
+            int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
             int max = mStatusBarState == StatusBarState.KEYGUARD
-                    ? Math.max(maxNotifications, maxQs)
-                    : maxQs;
+                    ? Math.max(maxNotificationPadding, maxQsPadding)
+                    : maxQsPadding;
             return (int) interpolate(getExpandedFraction(),
                     mQsMinExpansionHeight, max);
         } else if (mQsSizeChangeAnimator != null) {
@@ -1507,7 +1507,7 @@
         if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
             maxHeight = calculatePanelHeightQsExpanded();
         } else {
-            maxHeight = calculatePanelHeightShade();
+            maxHeight = Math.max(calculatePanelHeightShade(), calculatePanelHeightShadeExpanded());
         }
         maxHeight = Math.max(maxHeight, min);
         return maxHeight;
@@ -1524,7 +1524,15 @@
     @Override
     protected void onHeightUpdated(float expandedHeight) {
         if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
-            positionClockAndNotifications();
+            // Updating the clock position will set the top padding which might
+            // trigger a new panel height and re-position the clock.
+            // This is a circular dependency and should be avoided, otherwise we'll have
+            // a stack overflow.
+            if (mStackScrollerMeasuringPass > 2) {
+                if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
+            } else {
+                positionClockAndNotifications();
+            }
         }
         if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
                 && !mQsExpansionFromOverscroll) {
@@ -1568,12 +1576,18 @@
 
     private int calculatePanelHeightShade() {
         int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin
-                - mTopPaddingAdjustment;
+        int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
         maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
         return maxHeight;
     }
 
+    private int calculatePanelHeightShadeExpanded() {
+        return mNotificationStackScroller.getHeight()
+                - mNotificationStackScroller.getEmptyBottomMargin()
+                - mNotificationStackScroller.getTopPadding()
+                + mClockPositionAlgorithm.getExpandedClockBottom();
+    }
+
     private int calculatePanelHeightQsExpanded() {
         float notificationHeight = mNotificationStackScroller.getHeight()
                 - mNotificationStackScroller.getEmptyBottomMargin()
@@ -1598,8 +1612,7 @@
         }
         float totalHeight = Math.max(
                 maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD
-                        ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
-                        : 0)
+                        ? mClockPositionResult.stackScrollerPadding : 0)
                 + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow();
         if (totalHeight > mNotificationStackScroller.getHeight()) {
             float fullyCollapsedHeight = maxQsHeight
@@ -2272,8 +2285,9 @@
     }
 
     @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
         if (DEBUG) {
             Paint p = new Paint();
             p.setColor(Color.RED);
@@ -2288,6 +2302,9 @@
             p.setColor(Color.YELLOW);
             canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
                     calculatePanelHeightShade(), p);
+            p.setColor(Color.GRAY);
+            canvas.drawLine(0, calculatePanelHeightShadeExpanded(), getWidth(),
+                    calculatePanelHeightShadeExpanded(), p);
             p.setColor(Color.MAGENTA);
             canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
                     calculateQsTopPadding(), p);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index b82b0ee..00aff53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -176,8 +176,10 @@
 
         mTrackAnimator = ObjectAnimator.ofFloat();
         mTrackAnimator.addUpdateListener(mTrackAnimatorListener);
+        mTrackAnimator.setFloatValues(0);
         mButtonAnimator = ObjectAnimator.ofInt();
         mButtonAnimator.addUpdateListener(mButtonTranslationListener);
+        mButtonAnimator.setIntValues(0);
         mQuickScrubEndAnimator = new AnimatorSet();
         mQuickScrubEndAnimator.playTogether(mTrackAnimator, mButtonAnimator);
         mQuickScrubEndAnimator.setDuration(ANIM_DURATION_MS);
@@ -434,6 +436,7 @@
     private void animateEnd() {
         mButtonAnimator.setIntValues((int) mTranslation, 0);
         mTrackAnimator.setFloatValues(mTrackAlpha, 0);
+        mQuickScrubEndAnimator.setCurrentPlayTime(0);
         mQuickScrubEndAnimator.start();
     }
 
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 4ffe5fe..933c952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4985,7 +4985,7 @@
                                     notificationKey)) {
                                 // Show work challenge, do not run PendingIntent and
                                 // remove notification
-                                collapsePanel();
+                                collapseOnMainThread();
                                 return;
                             }
                         }
@@ -5026,11 +5026,7 @@
                     }
                 }
                 if (shouldCollapse()) {
-                    if (Looper.getMainLooper().isCurrentThread()) {
-                        collapsePanel();
-                    } else {
-                        mStackScroller.post(this::collapsePanel);
-                    }
+                    collapseOnMainThread();
                 }
 
                 try {
@@ -5058,6 +5054,14 @@
         }, afterKeyguardGone);
     }
 
+    private void collapseOnMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            collapsePanel();
+        } else {
+            mStackScroller.post(this::collapsePanel);
+        }
+    }
+
     private boolean shouldCollapse() {
         return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 944e888..fc043c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
-import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -77,7 +76,6 @@
         mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
     }
 
-    @FlakyTest
     @Test
     public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
         setRepliesFromRemoteInput(TEST_CHOICES);
@@ -182,7 +180,6 @@
         assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
     }
 
-    @FlakyTest
     @Test
     public void testMeasure_choiceWithThreeLines() {
         final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c0e5960..c56002e 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5278,6 +5278,7 @@
     // OS: P
     FIELD_QS_MODE = 1311;
 
+
     // OPEN: Settings->Developer Options->Default USB
     // CATEGORY: SETTINGS
     // OS: P
@@ -5317,6 +5318,16 @@
     // OS: P
     FIELD_LINKIFY_CALL_ID = 1319;
 
+    // FIELD: The compiler filter used when when optimizing the package.
+    //        Logged together with app transition events.
+    // OS: P
+    PACKAGE_OPTIMIZATION_COMPILATION_FILTER = 1320;
+
+    // FIELD: The reason for optimizing the package.
+    //        Logged together with app transition events.
+    // OS: P
+    PACKAGE_OPTIMIZATION_COMPILATION_REASON = 1321;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3419b80..84a8d45 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 import android.util.Slog;
 import android.view.InputDevice;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
@@ -296,7 +295,8 @@
                 // Already handled.
             } break;
             default:
-                throw new IllegalStateException("Illegal state: " + mCurrentState);
+                Slog.e(LOG_TAG, "Illegal state: " + mCurrentState);
+                clear(event, policyFlags);
         }
     }
 
@@ -649,8 +649,10 @@
         }
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
-                throw new IllegalStateException("Dragging state can be reached only if two "
+                Slog.e(LOG_TAG, "Dragging state can be reached only if two "
                         + "pointers are already down");
+                clear(event, policyFlags);
+                return;
             }
             case MotionEvent.ACTION_POINTER_DOWN: {
                 // We are in dragging state so we have two pointers and another one
@@ -741,8 +743,10 @@
     private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
-                throw new IllegalStateException("Delegating state can only be reached if "
+                Slog.e(LOG_TAG, "Delegating state can only be reached if "
                         + "there is at least one pointer down!");
+                clear(event, policyFlags);
+                return;
             }
             case MotionEvent.ACTION_UP: {
                 // Offset the event if we are doing a long press as the
@@ -1093,7 +1097,7 @@
             case STATE_GESTURE_DETECTING:
                 return "STATE_GESTURE_DETECTING";
             default:
-                throw new IllegalArgumentException("Unknown state: " + state);
+                return "Unknown state: " + state;
         }
     }
 
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9cd3621..c93f405 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3860,7 +3860,7 @@
     final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
         @Override
         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
-                boolean idle, int bucket) {
+                boolean idle, int bucket, int reason) {
             if (DEBUG_STANDBY) {
                 Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
                         bucket);
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index fc4d463..a6b71b7 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -678,7 +678,7 @@
     final class StandbyTracker extends AppIdleStateChangeListener {
         @Override
         public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
-                int bucket) {
+                int bucket, int reason) {
             if (DEBUG) {
                 Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
                         + (idle ? " idle" : " active") + " " + bucket);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0d6d2bd..6550d06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -85,7 +85,7 @@
     private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
     private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
     private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
-    private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+    private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
     private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
     private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
     private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6fcdc3e..d4307d7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22355,7 +22355,7 @@
         // Update the configuration with WM first and check if any of the stacks need to be resized
         // due to the configuration change. If so, resize the stacks now and do any relaunches if
         // necessary. This way we don't need to relaunch again afterwards in
-        // ensureActivityConfigurationLocked().
+        // ensureActivityConfiguration().
         if (mWindowManager != null) {
             final int[] resizedStacks =
                     mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
@@ -22383,7 +22383,7 @@
             }
 
             if (starting != null) {
-                kept = starting.ensureActivityConfigurationLocked(changes,
+                kept = starting.ensureActivityConfiguration(changes,
                         false /* preserveWindow */);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 07e27bc..3b2a22d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,6 +23,7 @@
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.IUidObserver;
+import android.app.KeyguardManager;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.usage.AppStandbyInfo;
@@ -32,16 +33,24 @@
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.DeviceConfigurationProto;
+import android.content.GlobalConfigurationProto;
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
 import android.content.pm.UserInfo;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -57,8 +66,11 @@
 import android.util.ArrayMap;
 import android.util.DebugUtils;
 import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
 
 import com.android.internal.util.HexDump;
+import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
 
 import java.io.BufferedReader;
@@ -75,6 +87,11 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityManager.RESIZE_MODE_USER;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -1835,17 +1852,112 @@
         }
     }
 
-    int runGetConfig(PrintWriter pw) throws RemoteException {
-        int days = 14;
-        String option = getNextOption();
-        if (option != null) {
-            if (!option.equals("--days")) {
-                throw new IllegalArgumentException("unrecognized option " + option);
-            }
+    private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
+            PrintWriter pw, Configuration config, DisplayManager dm) {
+        Point stableSize = dm.getStableDisplaySize();
+        long token = -1;
+        if (protoOutputStream != null) {
+            token = protoOutputStream.start(fieldId);
+            protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_WIDTH_PX, stableSize.x);
+            protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_HEIGHT_PX, stableSize.y);
+            protoOutputStream.write(DeviceConfigurationProto.STABLE_DENSITY_DPI,
+                    DisplayMetrics.DENSITY_DEVICE_STABLE);
+        }
+        if (pw != null) {
+            pw.print("stable-width-px: "); pw.println(stableSize.x);
+            pw.print("stable-height-px: "); pw.println(stableSize.y);
+            pw.print("stable-density-dpi: "); pw.println(DisplayMetrics.DENSITY_DEVICE_STABLE);
+        }
 
-            days = Integer.parseInt(getNextArgRequired());
-            if (days <= 0) {
-                throw new IllegalArgumentException("--days must be a positive integer");
+        MemInfoReader memreader = new MemInfoReader();
+        memreader.readMemInfo();
+        KeyguardManager kgm = mInternal.mContext.getSystemService(KeyguardManager.class);
+        if (protoOutputStream != null) {
+            protoOutputStream.write(DeviceConfigurationProto.TOTAL_RAM, memreader.getTotalSize());
+            protoOutputStream.write(DeviceConfigurationProto.LOW_RAM,
+                    ActivityManager.isLowRamDeviceStatic());
+            protoOutputStream.write(DeviceConfigurationProto.MAX_CORES,
+                    Runtime.getRuntime().availableProcessors());
+            protoOutputStream.write(DeviceConfigurationProto.HAS_SECURE_SCREEN_LOCK,
+                    kgm.isDeviceSecure());
+        }
+        if (pw != null) {
+            pw.print("total-ram: "); pw.println(memreader.getTotalSize());
+            pw.print("low-ram: "); pw.println(ActivityManager.isLowRamDeviceStatic());
+            pw.print("max-cores: "); pw.println(Runtime.getRuntime().availableProcessors());
+            pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
+        }
+
+        ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
+        if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
+            if (protoOutputStream != null) {
+                protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+                        configInfo.reqGlEsVersion);
+            }
+            if (pw != null) {
+                pw.print("opengl-version: 0x");
+                pw.println(Integer.toHexString(configInfo.reqGlEsVersion));
+            }
+        }
+
+        /*
+        GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
+        protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+                gl.glGetString(GL10.GL_VERSION));
+        String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
+        for (String ext : glExtensions.split(" ")) {
+            protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
+        }
+        */
+
+        PackageManager pm = mInternal.mContext.getPackageManager();
+        List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
+        for (int i = 0; i < slibs.size(); i++) {
+            if (protoOutputStream != null) {
+                protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
+                        slibs.get(i).getName());
+            }
+            if (pw != null) {
+                pw.print("shared-libraries: "); pw.println(slibs.get(i).getName());
+            }
+        }
+
+        FeatureInfo[] features = pm.getSystemAvailableFeatures();
+        for (int i = 0; i < features.length; i++) {
+            if (features[i].name != null) {
+                if (protoOutputStream != null) {
+                    protoOutputStream.write(DeviceConfigurationProto.FEATURES, features[i].name);
+                }
+                if (pw != null) {
+                    pw.print("features: "); pw.println(features[i].name);
+                }
+            }
+        }
+
+        if (protoOutputStream != null) {
+            protoOutputStream.end(token);
+        }
+    }
+
+    int runGetConfig(PrintWriter pw) throws RemoteException {
+        int days = -1;
+        boolean asProto = false;
+        boolean inclDevice = false;
+
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            if (opt.equals("--days")) {
+                days = Integer.parseInt(getNextArgRequired());
+                if (days <= 0) {
+                    throw new IllegalArgumentException("--days must be a positive integer");
+                }
+            } else if (opt.equals("--proto")) {
+                asProto = true;
+            } else if (opt.equals("--device")) {
+                inclDevice = true;
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
             }
         }
 
@@ -1855,18 +1967,38 @@
             return -1;
         }
 
-        pw.println("config: " + Configuration.resourceQualifierString(config));
-        pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+        DisplayManager dm = mInternal.mContext.getSystemService(DisplayManager.class);
+        Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+        DisplayMetrics metrics = new DisplayMetrics();
+        display.getMetrics(metrics);
 
-        final List<Configuration> recentConfigs = getRecentConfigurations(days);
-        final int recentConfigSize = recentConfigs.size();
-        if (recentConfigSize > 0) {
-            pw.println("recentConfigs:");
-        }
+        if (asProto) {
+            final ProtoOutputStream proto = new ProtoOutputStream(getOutFileDescriptor());
+            config.writeResConfigToProto(proto, GlobalConfigurationProto.RESOURCES, metrics);
+            if (inclDevice) {
+                writeDeviceConfig(proto, GlobalConfigurationProto.DEVICE, null, config, dm);
+            }
+            proto.flush();
 
-        for (int i = 0; i < recentConfigSize; i++) {
-            pw.println("  config: " + Configuration.resourceQualifierString(
-                    recentConfigs.get(i)));
+        } else {
+            pw.println("config: " + Configuration.resourceQualifierString(config, metrics));
+            pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+            if (inclDevice) {
+                writeDeviceConfig(null, -1, pw, config, dm);
+            }
+
+            if (days >= 0) {
+                final List<Configuration> recentConfigs = getRecentConfigurations(days);
+                final int recentConfigSize = recentConfigs.size();
+                if (recentConfigSize > 0) {
+                    pw.println("recentConfigs:");
+                    for (int i = 0; i < recentConfigSize; i++) {
+                        pw.println("  config: " + Configuration.resourceQualifierString(
+                                recentConfigs.get(i)));
+                    }
+                }
+            }
+
         }
         return 0;
     }
@@ -2729,8 +2861,11 @@
             pw.println("      Gets the process state of an app given its <UID>.");
             pw.println("  attach-agent <PROCESS> <FILE>");
             pw.println("    Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
-            pw.println("  get-config");
-            pw.println("      Rtrieve the configuration and any recent configurations of the device.");
+            pw.println("  get-config [--days N] [--device] [--proto]");
+            pw.println("      Retrieve the configuration and any recent configurations of the device.");
+            pw.println("      --days: also return last N days of configurations that have been seen.");
+            pw.println("      --device: also output global device configuration info.");
+            pw.println("      --proto: return result as a proto; does not include --days info.");
             pw.println("  supports-multiwindow");
             pw.println("      Returns true if the device supports multiwindow.");
             pw.println("  supports-split-screen-multi-window");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index db86f1a..978e344 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -23,6 +23,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
@@ -35,6 +37,9 @@
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromMemcg;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.dex.ArtManagerInternal;
+import android.content.pm.dex.PackageOptimizationInfo;
 import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.Looper;
@@ -47,6 +52,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 
@@ -68,7 +74,8 @@
     private static final long INVALID_START_TIME = -1;
 
     private static final int MSG_CHECK_VISIBILITY = 0;
-    private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 1;
+    private static final int MSG_LOG_APP_TRANSITION = 1;
+    private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 2;
 
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
@@ -93,6 +100,9 @@
     private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
             new SparseArray<>();
     private final H mHandler;
+
+    private ArtManagerInternal mArtManagerInternal;
+
     private final class H extends Handler {
 
         public H(Looper looper) {
@@ -106,12 +116,16 @@
                     final SomeArgs args = (SomeArgs) msg.obj;
                     checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
                     break;
+                case MSG_LOG_APP_TRANSITION:
+                    logAppTransition(msg.arg1, msg.arg2,
+                            (WindowingModeTransitionInfoSnapshot) msg.obj);
+                    break;
                 case MSG_LOG_APP_START_MEMORY_STATE_CAPTURE:
                     logAppStartMemoryStateCapture((WindowingModeTransitionInfo) msg.obj);
                     break;
             }
         }
-    };
+    }
 
     private final class WindowingModeTransitionInfo {
         private ActivityRecord launchedActivity;
@@ -125,6 +139,36 @@
         private boolean loggedStartingWindowDrawn;
     }
 
+    private final class WindowingModeTransitionInfoSnapshot {
+        final private ApplicationInfo applicationInfo;
+        final private String packageName;
+        final private String launchedActivityName;
+        final private String launchedActivityLaunchedFromPackage;
+        final private String launchedActivityLaunchToken;
+        final private String launchedActivityAppRecordRequiredAbi;
+        final private int reason;
+        final private int startingWindowDelayMs;
+        final private int bindApplicationDelayMs;
+        final private int windowsDrawnDelayMs;
+        final private int type;
+
+        private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
+            applicationInfo = info.launchedActivity.appInfo;
+            packageName = info.launchedActivity.packageName;
+            launchedActivityName = info.launchedActivity.info.name;
+            launchedActivityLaunchedFromPackage = info.launchedActivity.launchedFromPackage;
+            launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
+            launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
+                    ? null
+                    : info.launchedActivity.app.requiredAbi;
+            reason = info.reason;
+            startingWindowDelayMs = info.startingWindowDelayMs;
+            bindApplicationDelayMs = info.bindApplicationDelayMs;
+            windowsDrawnDelayMs = info.windowsDrawnDelayMs;
+            type = getTransitionType(info);
+        }
+    }
+
     ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
@@ -456,54 +500,80 @@
             if (type == -1) {
                 return;
             }
-            final LogMaker builder = new LogMaker(APP_TRANSITION);
-            builder.setPackageName(info.launchedActivity.packageName);
-            builder.setType(type);
-            builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
-            final boolean isInstantApp = info.launchedActivity.info.applicationInfo.isInstantApp();
-            if (info.launchedActivity.launchedFromPackage != null) {
-                builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
-                        info.launchedActivity.launchedFromPackage);
-            }
-            String launchToken = info.launchedActivity.info.launchToken;
-            if (launchToken != null) {
-                builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
-                info.launchedActivity.info.launchToken = null;
-            }
-            builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
-            builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
-                    mCurrentTransitionDeviceUptime);
-            builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
-            builder.setSubtype(info.reason);
-            if (info.startingWindowDelayMs != -1) {
-                builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
-                        info.startingWindowDelayMs);
-            }
-            if (info.bindApplicationDelayMs != -1) {
-                builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
-                        info.bindApplicationDelayMs);
-            }
-            builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
-            mMetricsLogger.write(builder);
-            StatsLog.write(
-                    StatsLog.APP_START_CHANGED,
-                    info.launchedActivity.appInfo.uid,
-                    info.launchedActivity.packageName,
-                    convertAppStartTransitionType(type),
-                    info.launchedActivity.info.name,
-                    info.launchedActivity.launchedFromPackage,
-                    isInstantApp,
-                    mCurrentTransitionDeviceUptime * 1000,
-                    info.reason,
-                    mCurrentTransitionDelayMs,
-                    info.startingWindowDelayMs,
-                    info.bindApplicationDelayMs,
-                    info.windowsDrawnDelayMs,
-                    launchToken);
+
+            // Take a snapshot of the transition info before sending it to the handler for logging.
+            // This will avoid any races with other operations that modify the ActivityRecord.
+            final WindowingModeTransitionInfoSnapshot infoSnapshot =
+                    new WindowingModeTransitionInfoSnapshot(info);
+            mHandler.obtainMessage(MSG_LOG_APP_TRANSITION, mCurrentTransitionDeviceUptime,
+                    mCurrentTransitionDelayMs, infoSnapshot).sendToTarget();
+
+            info.launchedActivity.info.launchToken = null;
             mHandler.obtainMessage(MSG_LOG_APP_START_MEMORY_STATE_CAPTURE, info).sendToTarget();
         }
     }
 
+    // This gets called on the handler without holding the activity manager lock.
+    private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
+            WindowingModeTransitionInfoSnapshot info) {
+        final LogMaker builder = new LogMaker(APP_TRANSITION);
+        builder.setPackageName(info.packageName);
+        builder.setType(info.type);
+        builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
+        final boolean isInstantApp = info.applicationInfo.isInstantApp();
+        if (info.launchedActivityLaunchedFromPackage != null) {
+            builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
+                    info.launchedActivityLaunchedFromPackage);
+        }
+        String launchToken = info.launchedActivityLaunchToken;
+        if (launchToken != null) {
+            builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
+        }
+        builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
+        builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+                currentTransitionDeviceUptime);
+        builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
+        builder.setSubtype(info.reason);
+        if (info.startingWindowDelayMs != -1) {
+            builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+                    info.startingWindowDelayMs);
+        }
+        if (info.bindApplicationDelayMs != -1) {
+            builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
+                    info.bindApplicationDelayMs);
+        }
+        builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
+        final ArtManagerInternal artManagerInternal = getArtManagerInternal();
+        final PackageOptimizationInfo packageOptimizationInfo =
+                (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
+                ? PackageOptimizationInfo.createWithNoInfo()
+                : artManagerInternal.getPackageOptimizationInfo(
+                        info.applicationInfo,
+                        info.launchedActivityAppRecordRequiredAbi);
+        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
+                packageOptimizationInfo.getCompilationReason());
+        builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
+                packageOptimizationInfo.getCompilationFilter());
+        mMetricsLogger.write(builder);
+        StatsLog.write(
+                StatsLog.APP_START_CHANGED,
+                info.applicationInfo.uid,
+                info.packageName,
+                convertAppStartTransitionType(info.type),
+                info.launchedActivityName,
+                info.launchedActivityLaunchedFromPackage,
+                isInstantApp,
+                currentTransitionDeviceUptime * 1000,
+                info.reason,
+                currentTransitionDelayMs,
+                info.startingWindowDelayMs,
+                info.bindApplicationDelayMs,
+                info.windowsDrawnDelayMs,
+                launchToken,
+                packageOptimizationInfo.getCompilationReason(),
+                packageOptimizationInfo.getCompilationFilter());
+    }
+
     private int convertAppStartTransitionType(int tronType) {
         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
             return StatsLog.APP_START_CHANGED__TYPE__COLD;
@@ -592,4 +662,14 @@
                         launchedActivity.appInfo.uid)
                 : null;
     }
+
+    private ArtManagerInternal getArtManagerInternal() {
+        if (mArtManagerInternal == null) {
+            // Note that this may be null.
+            // ArtManagerInternal is registered during PackageManagerService
+            // initialization which happens after ActivityManagerService.
+            mArtManagerInternal = LocalServices.getService(ArtManagerInternal.class);
+        }
+        return mArtManagerInternal;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 06faeb9..8cc9273 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -361,7 +361,7 @@
     private boolean mTurnScreenOn;
 
     /**
-     * Temp configs used in {@link #ensureActivityConfigurationLocked(int, boolean)}
+     * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
      */
     private final Configuration mTmpConfig = new Configuration();
     private final Rect mTmpBounds = new Rect();
@@ -2365,13 +2365,27 @@
         outBounds.offsetTo(left, 0 /* top */);
     }
 
+    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
+        return ensureActivityConfiguration(globalChanges, preserveWindow,
+                false /* ignoreStopState */);
+    }
+
     /**
-     * Make sure the given activity matches the current configuration. Returns false if the activity
-     * had to be destroyed.  Returns true if the configuration is the same, or the activity will
-     * remain running as-is for whatever reason. Ensures the HistoryRecord is updated with the
-     * correct configuration and all other bookkeeping is handled.
+     * Make sure the given activity matches the current configuration. Ensures the HistoryRecord
+     * is updated with the correct configuration and all other bookkeeping is handled.
+     *
+     * @param globalChanges The changes to the global configuration.
+     * @param preserveWindow If the activity window should be preserved on screen if the activity
+     *                       is relaunched.
+     * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped
+     *                        state. This is useful for the case where we know the activity will be
+     *                        visible soon and we want to ensure its configuration before we make it
+     *                        visible.
+     * @return True if the activity was relaunched and false if it wasn't relaunched because we
+     *         can't or the app handles the specific configuration that is changing.
      */
-    boolean ensureActivityConfigurationLocked(int globalChanges, boolean preserveWindow) {
+    boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
+            boolean ignoreStopState) {
         final ActivityStack stack = getStack();
         if (stack.mConfigWillChange) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -2387,8 +2401,7 @@
             return true;
         }
 
-        // Skip updating configuration for activity that are stopping or stopped.
-        if (mState == STOPPING || mState == STOPPED) {
+        if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Skipping config check stopped or stopping: " + this);
             return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 817b699..4987b33 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -123,7 +123,6 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1851,7 +1850,10 @@
                         // First: if this is not the current activity being started, make
                         // sure it matches the current configuration.
                         if (r != starting) {
-                            r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindows);
+                            // Ensure activity configuration ignoring stop state since we are
+                            // becoming visible.
+                            r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
+                                    true /* ignoreStopState */);
                         }
 
                         if (r.app == null || r.app.thread == null) {
@@ -4627,7 +4629,7 @@
                     (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
             for (; activityIndex >= 0; --activityIndex) {
                 final ActivityRecord r = activities.get(activityIndex);
-                updatedConfig |= r.ensureActivityConfigurationLocked(0 /* globalChanges */,
+                updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
                         preserveWindow);
                 if (r.fullscreen) {
                     behindFullscreen = true;
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index d84f487..c6947ab 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -369,7 +369,7 @@
             }
 
             if (starting != null) {
-                starting.ensureActivityConfigurationLocked(0 /* globalChanges */,
+                starting.ensureActivityConfiguration(0 /* globalChanges */,
                         false /* preserveWindow */);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a07afde..6f6e0d9 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -537,7 +537,7 @@
             if (updatedConfig) {
                 final ActivityRecord r = topRunningActivityLocked();
                 if (r != null && !deferResume) {
-                    kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */,
+                    kept = r.ensureActivityConfiguration(0 /* globalChanges */,
                             preserveWindow);
                     mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
                             !PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 740866c..017fada 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2240,7 +2240,7 @@
 
         @Override
         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
-                boolean idle, int bucket) {
+                boolean idle, int bucket, int reason) {
             final int uid = mLocalPM.getPackageUid(packageName,
                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
             if (uid < 0) {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index bd8fe28..ed29a4c 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -186,7 +186,8 @@
     private final class AppIdleStateChangeListener
             extends UsageStatsManagerInternal.AppIdleStateChangeListener {
         @Override
-        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+                int reason) {
             boolean changed = false;
             synchronized (mLock) {
                 if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 0dab528..5267f54 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1704,7 +1704,6 @@
             mStarted = false;
             mSingleShot = false;
             native_stop();
-            mTimeToFirstFix = 0;
             mLastFixTime = 0;
 
             // reset SV count to zero
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 838aa70..d1c40cc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -80,6 +80,7 @@
 import android.security.keystore.KeyProtection;
 import android.security.keystore.UserNotAuthenticatedException;
 import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.service.gatekeeper.GateKeeperResponse;
@@ -2005,8 +2006,8 @@
         mRecoverableKeyStoreManager.setRecoveryStatus(alias, status);
     }
 
-    public Map getRecoveryStatus(@Nullable String packageName) throws RemoteException {
-        return mRecoverableKeyStoreManager.getRecoveryStatus(packageName);
+    public Map getRecoveryStatus() throws RemoteException {
+        return mRecoverableKeyStoreManager.getRecoveryStatus();
     }
 
     @Override
@@ -2041,6 +2042,15 @@
                 vaultParams, vaultChallenge, secrets);
     }
 
+    @Override
+    public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId,
+            @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
+            throws RemoteException {
+        return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets);
+    }
+
     public void closeSession(@NonNull String sessionId) throws RemoteException {
         mRecoverableKeyStoreManager.closeSession(sessionId);
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index dee24c7..8efce86 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -289,6 +289,7 @@
                 .setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS)
                 .setCounterId(counterId)
                 .setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey))
+                .setTrustedHardwareCertPath(certPath)
                 .setServerParams(vaultHandle)
                 .setKeyChainProtectionParams(metadataList)
                 .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys))
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 89e2deb..a7d32ed 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -273,12 +273,16 @@
      *
      * @param key The bytes of the key.
      * @return The key.
-     * @throws NoSuchAlgorithmException if the public key algorithm is unavailable.
      * @throws InvalidKeySpecException if the bytes of the key are not a valid key.
      */
-    public static PublicKey deserializePublicKey(byte[] key)
-            throws NoSuchAlgorithmException, InvalidKeySpecException {
-        KeyFactory keyFactory = KeyFactory.getInstance(PUBLIC_KEY_FACTORY_ALGORITHM);
+    public static PublicKey deserializePublicKey(byte[] key) throws InvalidKeySpecException {
+        KeyFactory keyFactory;
+        try {
+            keyFactory = KeyFactory.getInstance(PUBLIC_KEY_FACTORY_ALGORITHM);
+        } catch (NoSuchAlgorithmException e) {
+            // Should not happen
+            throw new RuntimeException(e);
+        }
         X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
         return keyFactory.generatePublic(publicKeySpec);
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 35954a5..a462cfc 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -34,6 +34,7 @@
 import android.os.UserHandle;
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.RecoveryController;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.KeyStore;
@@ -57,7 +58,10 @@
 import java.security.PublicKey;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertPath;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
@@ -285,16 +289,14 @@
     }
 
     /**
-     * Gets recovery status for caller or other application {@code packageName}.
-     * @param packageName which recoverable keys statuses will be returned.
+     * Returns recovery statuses for all keys belonging to the calling uid.
      *
-     * @return {@code Map} from KeyStore alias to recovery status.
+     * @return {@link Map} from key alias to recovery status. Recovery status is one of
+     *     {@link RecoveryController#RECOVERY_STATUS_SYNCED},
+     *     {@link RecoveryController#RECOVERY_STATUS_SYNC_IN_PROGRESS} or
+     *     {@link RecoveryController#RECOVERY_STATUS_PERMANENT_FAILURE}.
      */
-    public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
-            throws RemoteException {
-        // Any application should be able to check status for its own keys.
-        // If caller is a recovery agent it can check statuses for other packages, but
-        // only for recoverable keys it manages.
+    public @NonNull Map<String, Integer> getRecoveryStatus() throws RemoteException {
         return mDatabase.getStatusForAllKeys(Binder.getCallingUid());
     }
 
@@ -351,7 +353,7 @@
     }
 
     /**
-     * Initializes recovery session.
+     * Initializes recovery session given the X509-encoded public key of the recovery service.
      *
      * @param sessionId A unique ID to identify the recovery session.
      * @param verifierPublicKey X509-encoded public key.
@@ -359,6 +361,8 @@
      * @param vaultChallenge Challenge issued by vault service.
      * @param secrets Lock-screen hashes. For now only a single secret is supported.
      * @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
+     * @deprecated Use {@link #startRecoverySessionWithCertPath(String, RecoveryCertPath, byte[],
+     *         byte[], List)} instead.
      *
      * @hide
      */
@@ -380,11 +384,9 @@
         PublicKey publicKey;
         try {
             publicKey = KeySyncUtils.deserializePublicKey(verifierPublicKey);
-        } catch (NoSuchAlgorithmException e) {
-            // Should never happen
-            throw new RuntimeException(e);
         } catch (InvalidKeySpecException e) {
-            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 key");
+            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+                    "Not a valid X509 key");
         }
         // The raw public key bytes contained in vaultParams must match the ones given in
         // verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned
@@ -417,6 +419,52 @@
     }
 
     /**
+     * Initializes recovery session given the certificate path of the recovery service.
+     *
+     * @param sessionId A unique ID to identify the recovery session.
+     * @param verifierCertPath The certificate path of the recovery service.
+     * @param vaultParams Additional params associated with vault.
+     * @param vaultChallenge Challenge issued by vault service.
+     * @param secrets Lock-screen hashes. For now only a single secret is supported.
+     * @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
+     *
+     * @hide
+     */
+    public @NonNull byte[] startRecoverySessionWithCertPath(
+            @NonNull String sessionId,
+            @NonNull RecoveryCertPath verifierCertPath,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws RemoteException {
+        checkRecoverKeyStorePermission();
+
+        CertPath certPath;
+        try {
+            certPath = verifierCertPath.getCertPath();
+        } catch (CertificateException e) {
+            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+                    "Failed decode the certificate path");
+        }
+
+        // TODO: Validate the cert path according to the root of trust
+
+        if (certPath.getCertificates().isEmpty()) {
+            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+                    "The given CertPath is empty");
+        }
+        byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
+        if (verifierPublicKey == null) {
+            Log.e(TAG, "Failed to encode verifierPublicKey");
+            throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+                    "Failed to encode verifierPublicKey");
+        }
+
+        return startRecoverySession(
+                sessionId, verifierPublicKey, vaultParams, vaultChallenge, secrets);
+    }
+
+    /**
      * Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
      * service.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f29e0bb..ab55553 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3947,7 +3947,8 @@
             extends UsageStatsManagerInternal.AppIdleStateChangeListener {
 
         @Override
-        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+                int reason) {
             try {
                 final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index e290272..2ece2b2 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -19,18 +19,20 @@
 import android.Manifest;
 import android.annotation.UserIdInt;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.ArtManager.ProfileType;
+import android.content.pm.dex.ArtManagerInternal;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.dex.PackageOptimizationInfo;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.content.pm.IPackageManager;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -42,8 +44,13 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
+
+import dalvik.system.DexFile;
+
+import dalvik.system.VMRuntime;
 import java.io.File;
 import java.io.FileNotFoundException;
 import libcore.io.IoUtils;
@@ -86,6 +93,8 @@
         mInstaller = installer;
         mInstallLock = installLock;
         mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+
+        LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
     }
 
     @Override
@@ -397,4 +406,30 @@
         }
         return result;
     }
+
+    private class ArtManagerInternalImpl extends ArtManagerInternal {
+        @Override
+        public PackageOptimizationInfo getPackageOptimizationInfo(
+                ApplicationInfo info, String abi) {
+            String compilationReason;
+            String compilationFilter;
+            try {
+                String isa = VMRuntime.getInstructionSet(abi);
+                String[] stats = DexFile.getDexFileOptimizationStatus(info.getBaseCodePath(), isa);
+                compilationFilter = stats[0];
+                compilationReason = stats[1];
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
+                compilationFilter = "error";
+                compilationReason = "error";
+            } catch (IllegalArgumentException e) {
+                Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath()
+                        + " due to an invalid abi " + abi, e);
+                compilationFilter = "error";
+                compilationReason = "error";
+            }
+
+            return new PackageOptimizationInfo(compilationFilter, compilationReason);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d6359b8..95c30d1 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -418,10 +418,11 @@
     private void addNetworkStats(
             int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
         int size = stats.size();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
         for (int j = 0; j < size; j++) {
             stats.getValues(j, entry);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
             e.writeInt(entry.uid);
             if (withFGBG) {
                 e.writeInt(entry.set);
@@ -500,10 +501,11 @@
     private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
         final KernelWakelockStats wakelockStats =
                 mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
             String name = ent.getKey();
             KernelWakelockStats.Entry kws = ent.getValue();
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
             e.writeString(name);
             e.writeInt(kws.mCount);
             e.writeInt(kws.mVersion);
@@ -576,8 +578,9 @@
 
     private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (UidTraffic traffic : info.getUidTraffic()) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
             e.writeInt(traffic.getUid());
             e.writeLong(traffic.getRxBytes());
             e.writeLong(traffic.getTxBytes());
@@ -605,11 +608,12 @@
     }
 
     private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
             long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
             if (clusterTimeMs != null) {
                 for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
-                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+                    StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
                     e.writeInt(cluster);
                     e.writeInt(speed);
                     e.writeLong(clusterTimeMs[speed]);
@@ -630,7 +634,7 @@
                 SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
                 mWifiManager.requestActivityInfo(wifiReceiver);
                 final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
-                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+                StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
                 e.writeLong(wifiInfo.getTimeStamp());
                 e.writeInt(wifiInfo.getStackState());
                 e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -655,7 +659,7 @@
             SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
             mTelephony.requestModemActivityInfo(modemReceiver);
             final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
             e.writeLong(modemInfo.getTimestamp());
             e.writeLong(modemInfo.getSleepTimeMillis());
             e.writeLong(modemInfo.getIdleTimeMillis());
@@ -672,7 +676,7 @@
 
     private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
         BluetoothActivityEnergyInfo info = pullBluetoothData();
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
         e.writeLong(info.getTimeStamp());
         e.writeInt(info.getBluetoothStackState());
         e.writeLong(info.getControllerTxTimeMillis());
@@ -695,13 +699,13 @@
     }
 
     private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
         e.writeLong(SystemClock.elapsedRealtime());
         pulledData.add(e);
     }
 
     private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 3);
         e.writeLong(mStatFsData.getAvailableBytes());
         e.writeLong(mStatFsSystem.getAvailableBytes());
         e.writeLong(mStatFsTemp.getAvailableBytes());
@@ -709,7 +713,7 @@
     }
 
     private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
-        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+        StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
         e.writeLong(SystemClock.uptimeMillis());
         pulledData.add(e);
     }
@@ -718,8 +722,9 @@
         List<ProcessMemoryState> processMemoryStates =
                 LocalServices.getService(ActivityManagerInternal.class)
                         .getMemoryStateForProcesses();
+        long elapsedNanos = SystemClock.elapsedRealtimeNanos();
         for (ProcessMemoryState processMemoryState : processMemoryStates) {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 8 /* fields */);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
             e.writeInt(processMemoryState.uid);
             e.writeString(processMemoryState.processName);
             e.writeInt(processMemoryState.oomScore);
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index f72d8ee..d4cbe7b 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.print.DumpUtils.writePrinterId;
 import static com.android.internal.util.dump.DumpUtils.writeComponentName;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
@@ -33,8 +34,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
-import android.os.Looper;
-import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -86,8 +85,6 @@
 
     private final RemotePrintServiceClient mPrintServiceClient;
 
-    private final Handler mHandler;
-
     private IPrintService mPrintService;
 
     private boolean mBinding;
@@ -128,7 +125,6 @@
         mIntent = new Intent().setComponent(mComponentName);
         mUserId = userId;
         mSpooler = spooler;
-        mHandler = new MyHandler(context.getMainLooper());
         mPrintServiceClient = new RemotePrintServiceClient(this);
     }
 
@@ -137,7 +133,8 @@
     }
 
     public void destroy() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleDestroy, this));
     }
 
     private void handleDestroy() {
@@ -163,7 +160,8 @@
 
     @Override
     public void binderDied() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleBinderDied, this));
     }
 
     private void handleBinderDied() {
@@ -177,7 +175,8 @@
     }
 
     public void onAllPrintJobsHandled() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleOnAllPrintJobsHandled, this));
     }
 
     private void handleOnAllPrintJobsHandled() {
@@ -209,8 +208,8 @@
     }
 
     public void onRequestCancelPrintJob(PrintJobInfo printJob) {
-        mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB,
-                printJob).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleRequestCancelPrintJob, this, printJob));
     }
 
     private void handleRequestCancelPrintJob(final PrintJobInfo printJob) {
@@ -235,8 +234,8 @@
     }
 
     public void onPrintJobQueued(PrintJobInfo printJob) {
-        mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
-                printJob).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleOnPrintJobQueued, this, printJob));
     }
 
     private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
@@ -262,7 +261,8 @@
     }
 
     public void createPrinterDiscoverySession() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleCreatePrinterDiscoverySession, this));
     }
 
     private void handleCreatePrinterDiscoverySession() {
@@ -288,7 +288,8 @@
     }
 
     public void destroyPrinterDiscoverySession() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleDestroyPrinterDiscoverySession, this));
     }
 
     private void handleDestroyPrinterDiscoverySession() {
@@ -325,8 +326,8 @@
     }
 
     public void startPrinterDiscovery(List<PrinterId> priorityList) {
-        mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
-                priorityList).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleStartPrinterDiscovery, this, priorityList));
     }
 
     private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) {
@@ -356,7 +357,8 @@
     }
 
     public void stopPrinterDiscovery() {
-        mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleStopPrinterDiscovery, this));
     }
 
     private void handleStopPrinterDiscovery() {
@@ -387,8 +389,8 @@
     }
 
     public void validatePrinters(List<PrinterId> printerIds) {
-        mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
-                printerIds).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleValidatePrinters, this, printerIds));
     }
 
     private void handleValidatePrinters(final List<PrinterId> printerIds) {
@@ -413,8 +415,8 @@
     }
 
     public void startPrinterStateTracking(@NonNull PrinterId printerId) {
-        mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
-                printerId).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleStartPrinterStateTracking, this, printerId));
     }
 
     /**
@@ -424,8 +426,8 @@
      * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
      */
     public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
-        mHandler.obtainMessage(MyHandler.MSG_REQUEST_CUSTOM_PRINTER_ICON,
-                printerId).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleRequestCustomPrinterIcon, this, printerId));
     }
 
     /**
@@ -481,8 +483,8 @@
     }
 
     public void stopPrinterStateTracking(PrinterId printerId) {
-        mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
-                printerId).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                RemotePrintService::handleStopPrinterStateTracking, this, printerId));
     }
 
     private void handleStopPrinterStateTracking(final PrinterId printerId) {
@@ -672,96 +674,6 @@
         }
     }
 
-    private final class MyHandler extends Handler {
-        public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
-        public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
-        public static final int MSG_START_PRINTER_DISCOVERY = 3;
-        public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
-        public static final int MSG_VALIDATE_PRINTERS = 5;
-        public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
-        public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
-        public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8;
-        public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9;
-        public static final int MSG_ON_PRINT_JOB_QUEUED = 10;
-        public static final int MSG_DESTROY = 11;
-        public static final int MSG_BINDER_DIED = 12;
-        public static final int MSG_REQUEST_CUSTOM_PRINTER_ICON = 13;
-
-        public MyHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public void handleMessage(Message message) {
-            if (mDestroyed) {
-                Slog.w(LOG_TAG, "Not handling " + message + " as service for " + mComponentName
-                        + " is already destroyed");
-                return;
-            }
-            switch (message.what) {
-                case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
-                    handleCreatePrinterDiscoverySession();
-                } break;
-
-                case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
-                    handleDestroyPrinterDiscoverySession();
-                } break;
-
-                case MSG_START_PRINTER_DISCOVERY: {
-                    List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj;
-                    handleStartPrinterDiscovery(priorityList);
-                } break;
-
-                case MSG_STOP_PRINTER_DISCOVERY: {
-                    handleStopPrinterDiscovery();
-                } break;
-
-                case MSG_VALIDATE_PRINTERS: {
-                    List<PrinterId> printerIds = (List<PrinterId>) message.obj;
-                    handleValidatePrinters(printerIds);
-                } break;
-
-                case MSG_START_PRINTER_STATE_TRACKING: {
-                    PrinterId printerId = (PrinterId) message.obj;
-                    handleStartPrinterStateTracking(printerId);
-                } break;
-
-                case MSG_STOP_PRINTER_STATE_TRACKING: {
-                    PrinterId printerId = (PrinterId) message.obj;
-                    handleStopPrinterStateTracking(printerId);
-                } break;
-
-                case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
-                    handleOnAllPrintJobsHandled();
-                } break;
-
-                case MSG_ON_REQUEST_CANCEL_PRINT_JOB: {
-                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                    handleRequestCancelPrintJob(printJob);
-                } break;
-
-                case MSG_ON_PRINT_JOB_QUEUED: {
-                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                    handleOnPrintJobQueued(printJob);
-                } break;
-
-                case MSG_DESTROY: {
-                    handleDestroy();
-                } break;
-
-                case MSG_BINDER_DIED: {
-                    handleBinderDied();
-                } break;
-
-                case MSG_REQUEST_CUSTOM_PRINTER_ICON: {
-                    PrinterId printerId = (PrinterId) message.obj;
-                    handleRequestCustomPrinterIcon(printerId);
-                } break;
-            }
-        }
-    }
-
     private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
         private final WeakReference<RemotePrintService> mWeakService;
 
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 3c09cb1..62185d7 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -45,7 +45,6 @@
 import android.os.IBinder.DeathRecipient;
 import android.os.IInterface;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -81,6 +80,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
 import com.android.server.print.RemotePrintServiceRecommendationService
         .RemotePrintServiceRecommendationServiceCallbacks;
@@ -93,6 +93,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.IntSupplier;
 
 /**
  * Represents the print state for a user.
@@ -134,8 +135,6 @@
 
     private final RemotePrintSpooler mSpooler;
 
-    private final Handler mHandler;
-
     private PrinterDiscoverySessionMediator mPrinterDiscoverySession;
 
     private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
@@ -161,7 +160,6 @@
         mUserId = userId;
         mLock = lock;
         mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this);
-        mHandler = new UserStateHandler(context.getMainLooper());
 
         synchronized (mLock) {
             readInstalledPrintServicesLocked();
@@ -172,9 +170,7 @@
         // Some print services might have gotten installed before the User State came up
         prunePrintServices();
 
-        synchronized (mLock) {
-            onConfigurationChangedLocked();
-        }
+        onConfigurationChanged();
     }
 
     public void increasePriority() {
@@ -695,18 +691,22 @@
     @Override
     public void onPrintJobStateChanged(PrintJobInfo printJob) {
         mPrintJobForAppCache.onPrintJobStateChanged(printJob);
-        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
-                printJob.getAppId(), 0, printJob.getId()).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                UserState::handleDispatchPrintJobStateChanged,
+                this, printJob.getId(),
+                PooledLambda.obtainSupplier(printJob.getAppId()).recycleOnUse()));
     }
 
     public void onPrintServicesChanged() {
-        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                UserState::handleDispatchPrintServicesChanged, this));
     }
 
     @Override
     public void onPrintServiceRecommendationsUpdated(List<RecommendationInfo> recommendations) {
-        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED,
-                0, 0, recommendations).sendToTarget();
+        Handler.getMain().sendMessage(obtainMessage(
+                UserState::handleDispatchPrintServiceRecommendationsUpdated,
+                this, recommendations));
     }
 
     @Override
@@ -771,8 +771,8 @@
             mActiveServices.remove(service.getComponentName());
 
             // The service might need to be restarted if it died because of an update
-            mHandler.sendMessageDelayed(
-                    mHandler.obtainMessage(UserStateHandler.MSG_CHECK_CONFIG_CHANGED),
+            Handler.getMain().sendMessageDelayed(obtainMessage(
+                    UserState::onConfigurationChanged, this),
                     SERVICE_RESTART_DELAY_MILLIS);
 
             // No session - nothing to do.
@@ -1112,24 +1112,26 @@
         }
     }
 
-    private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) {
+    private void handleDispatchPrintJobStateChanged(
+            PrintJobId printJobId, IntSupplier appIdSupplier) {
+        int appId = appIdSupplier.getAsInt();
         final List<PrintJobStateChangeListenerRecord> records;
         synchronized (mLock) {
             if (mPrintJobStateChangeListenerRecords == null) {
                 return;
             }
-            records = new ArrayList<PrintJobStateChangeListenerRecord>(
-                    mPrintJobStateChangeListenerRecords);
+            records = new ArrayList<>(mPrintJobStateChangeListenerRecords);
         }
         final int recordCount = records.size();
         for (int i = 0; i < recordCount; i++) {
             PrintJobStateChangeListenerRecord record = records.get(i);
             if (record.appId == PrintManager.APP_ID_ANY
-                    || record.appId == appId)
-            try {
-                record.listener.onPrintJobStateChanged(printJobId);
-            } catch (RemoteException re) {
-                Log.e(LOG_TAG, "Error notifying for print job state change", re);
+                    || record.appId == appId) {
+                try {
+                    record.listener.onPrintJobStateChanged(printJobId);
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error notifying for print job state change", re);
+                }
             }
         }
     }
@@ -1177,38 +1179,9 @@
         }
     }
 
-    private final class UserStateHandler extends Handler {
-        public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
-        public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
-        public static final int MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED = 3;
-        public static final int MSG_CHECK_CONFIG_CHANGED = 4;
-
-        public UserStateHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
-                    PrintJobId printJobId = (PrintJobId) message.obj;
-                    final int appId = message.arg1;
-                    handleDispatchPrintJobStateChanged(printJobId, appId);
-                    break;
-                case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
-                    handleDispatchPrintServicesChanged();
-                    break;
-                case MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED:
-                    handleDispatchPrintServiceRecommendationsUpdated(
-                            (List<RecommendationInfo>) message.obj);
-                    break;
-                case MSG_CHECK_CONFIG_CHANGED:
-                    synchronized (mLock) {
-                        onConfigurationChangedLocked();
-                    }
-                default:
-                    // not reached
-            }
+    private void onConfigurationChanged() {
+        synchronized (mLock) {
+            onConfigurationChangedLocked();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index 90db2a3..796d364 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,6 +15,9 @@
  */
 package com.android.server;
 
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+
 import static com.android.server.AppStateTracker.TARGET_OP;
 
 import static org.junit.Assert.assertEquals;
@@ -612,7 +615,7 @@
 
         // Exempt package 2 on user-10.
         mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
-                UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+                UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
 
         areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
         areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -624,7 +627,7 @@
 
         // Exempt package 1 on user-0.
         mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
-                UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+                UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
 
         areRestricted(instance, UID_1, PACKAGE_1, NONE);
         areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -632,7 +635,7 @@
 
         // Unexempt package 2 on user-10.
         mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
-                UsageStatsManager.STANDBY_BUCKET_ACTIVE);
+                UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
 
         areRestricted(instance, UID_1, PACKAGE_1, NONE);
         areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -644,9 +647,9 @@
         mPowerSaveObserver.accept(getPowerSaveState());
 
         mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
-                UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+                UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
         mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
-                UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+                UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
 
         setAppOps(UID_1, PACKAGE_1, true);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5161114..8b21059 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -161,7 +161,7 @@
         when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
         mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
         mActivity.info.maxAspectRatio = aspectRatio;
-        mActivity.ensureActivityConfigurationLocked(
+        mActivity.ensureActivityConfiguration(
                 0 /* globalChanges */, false /* preserveWindow */);
         assertEquals(expectedActivityBounds, mActivity.getBounds());
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 3c35c5b..b67659d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -45,6 +45,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.support.test.filters.SmallTest;
 import android.support.test.InstrumentationRegistry;
@@ -67,6 +68,10 @@
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.concurrent.Executors;
 import java.util.Map;
 import java.util.Random;
@@ -335,6 +340,46 @@
     }
 
     @Test
+    public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
+        mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                TEST_SESSION_ID,
+                RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+                TEST_VAULT_PARAMS,
+                TEST_VAULT_CHALLENGE,
+                ImmutableList.of(
+                        new KeyChainProtectionParams(
+                                TYPE_LOCKSCREEN,
+                                UI_FORMAT_PASSWORD,
+                                KeyDerivationParams.createSha256Params(TEST_SALT),
+                                TEST_SECRET)));
+
+        assertEquals(1, mRecoverySessionStorage.size());
+        RecoverySessionStorage.Entry entry =
+                mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
+        assertArrayEquals(TEST_SECRET, entry.getLskfHash());
+        assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
+    }
+
+    @Test
+    public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
+        mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                TEST_SESSION_ID,
+                RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+                TEST_VAULT_PARAMS,
+                TEST_VAULT_CHALLENGE,
+                ImmutableList.of(
+                        new KeyChainProtectionParams(
+                                TYPE_LOCKSCREEN,
+                                UI_FORMAT_PASSWORD,
+                                KeyDerivationParams.createSha256Params(TEST_SALT),
+                                TEST_SECRET)));
+
+        verify(mMockContext, times(2))
+                .enforceCallingOrSelfPermission(
+                        eq(Manifest.permission.RECOVER_KEYSTORE), any());
+    }
+
+    @Test
     public void startRecoverySession_storesTheSessionInfo() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
@@ -432,6 +477,66 @@
     }
 
     @Test
+    public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception {
+        try {
+            mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                    TEST_SESSION_ID,
+                    RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+                    TEST_VAULT_PARAMS,
+                    TEST_VAULT_CHALLENGE,
+                    ImmutableList.of());
+            fail("should have thrown");
+        } catch (UnsupportedOperationException e) {
+            assertThat(e.getMessage()).startsWith(
+                    "Only a single KeyChainProtectionParams is supported");
+        }
+    }
+
+    @Test
+    public void startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch() throws Exception {
+        byte[] vaultParams = TEST_VAULT_PARAMS.clone();
+        vaultParams[1] ^= (byte) 1;  // Flip 1 bit
+        try {
+            mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                    TEST_SESSION_ID,
+                    RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+                    vaultParams,
+                    TEST_VAULT_CHALLENGE,
+                    ImmutableList.of(
+                            new KeyChainProtectionParams(
+                                    TYPE_LOCKSCREEN,
+                                    UI_FORMAT_PASSWORD,
+                                    KeyDerivationParams.createSha256Params(TEST_SALT),
+                                    TEST_SECRET)));
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("do not match");
+        }
+    }
+
+    @Test
+    public void startRecoverySessionWithCertPath_throwsIfEmptyCertPath() throws Exception {
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>());
+        try {
+            mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+                    TEST_SESSION_ID,
+                    RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
+                    TEST_VAULT_PARAMS,
+                    TEST_VAULT_CHALLENGE,
+                    ImmutableList.of(
+                            new KeyChainProtectionParams(
+                                    TYPE_LOCKSCREEN,
+                                    UI_FORMAT_PASSWORD,
+                                    KeyDerivationParams.createSha256Params(TEST_SALT),
+                                    TEST_SECRET)));
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("CertPath is empty");
+        }
+    }
+
+    @Test
     public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
         try {
             mRecoverableKeyStoreManager.recoverKeys(
@@ -648,12 +753,12 @@
         WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
         mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
         Map<String, Integer> statuses =
-                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
+                mRecoverableKeyStoreManager.getRecoveryStatus();
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status);
 
         mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2);
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
+        statuses = mRecoverableKeyStoreManager.getRecoveryStatus();
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status2); // updated
     }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7b06648..36504ac 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,10 +16,13 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 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;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -92,18 +95,18 @@
         AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
 
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
-                UsageStatsManager.REASON_USAGE);
+                REASON_MAIN_USAGE);
         // ACTIVE means not idle
         assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
 
         aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
-                UsageStatsManager.REASON_USAGE);
+                REASON_MAIN_USAGE);
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
-                REASON_TIMEOUT);
+                REASON_MAIN_TIMEOUT);
 
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
-        assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
 
         // RARE is considered idle
         assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
@@ -115,7 +118,7 @@
         aih = new AppIdleHistory(mStorageDir, 4000);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
-        assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+        assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
 
         assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
         assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
@@ -133,4 +136,18 @@
         assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
         assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
     }
+
+    public void testReason() throws Exception {
+        AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+        aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+        assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
+                aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
+        aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
+                REASON_MAIN_TIMEOUT);
+        aih.writeAppIdleTimes(USER_ID);
+
+        aih = new AppIdleHistory(mStorageDir, 5000);
+        assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
+    }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index cbbdca6..edf1f74 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,11 +18,11 @@
 
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_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;
@@ -410,11 +410,11 @@
         setChargingState(mController, false);
         // Set it to timeout or usage, so that prediction can override it
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
-                REASON_TIMEOUT, 1 * HOUR_MS);
+                REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+                REASON_MAIN_PREDICTED, 1 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
 
         // Fast forward 12 hours
@@ -440,28 +440,28 @@
         setChargingState(mController, false);
         // Can force to NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
-                REASON_FORCED, 1 * HOUR_MS);
+                REASON_MAIN_FORCED, 1 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
 
         // Prediction can't override FORCED reason
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_FORCED, 1 * HOUR_MS);
+                REASON_MAIN_FORCED, 1 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
-                REASON_PREDICTED, 1 * HOUR_MS);
+                REASON_MAIN_PREDICTED, 1 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
 
         // Prediction can't override NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
-                REASON_DEFAULT, 2 * HOUR_MS);
+                REASON_MAIN_DEFAULT, 2 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_PREDICTED, 2 * HOUR_MS);
+                REASON_MAIN_PREDICTED, 2 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
 
         // Prediction can't set to NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_USAGE, 2 * HOUR_MS);
+                REASON_MAIN_USAGE, 2 * HOUR_MS);
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
-                REASON_PREDICTED, 2 * HOUR_MS);
+                REASON_MAIN_PREDICTED, 2 * HOUR_MS);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
     }
 
@@ -474,7 +474,7 @@
 
         mInjector.mElapsedRealtime = 2000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // bucketing works after timeout
@@ -483,7 +483,7 @@
         assertBucket(STANDBY_BUCKET_WORKING_SET);
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_FREQUENT);
     }
 
@@ -498,15 +498,15 @@
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
-                REASON_PREDICTED, 1000);
+                REASON_MAIN_PREDICTED, 1000);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+                REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
         assertBucket(STANDBY_BUCKET_WORKING_SET);
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+                REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
         assertBucket(STANDBY_BUCKET_FREQUENT);
     }
 
@@ -527,18 +527,18 @@
         // Still in ACTIVE after first USER_INTERACTION times out
         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // Both timed out, so NOTIFICATION_SEEN timeout should be effective
         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_WORKING_SET);
 
         mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_RARE);
     }
 
@@ -561,7 +561,7 @@
         // Predict to ACTIVE
         mInjector.mElapsedRealtime += 1000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_PREDICTED, mInjector.mElapsedRealtime);
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // CheckIdleStates should not change the prediction
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 8e5a418..fd28b65 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.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_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -112,8 +114,10 @@
         // Standby bucket
         @UsageStatsManager.StandbyBuckets
         int currentBucket;
-        // Reason for setting the standby bucket. TODO: Switch to int.
-        String bucketingReason;
+        // Reason for setting the standby bucket. The value here is a combination of
+        // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
+        // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
+        int bucketingReason;
         // In-memory only, last bucket for which the listeners were informed
         int lastInformedBucket;
         // The last time a job was run for this app, using elapsed timebase
@@ -212,13 +216,14 @@
      * @param appUsageHistory the usage record for the app being updated
      * @param packageName name of the app being updated, for logging purposes
      * @param newBucket the bucket to set the app to
+     * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
      * @param elapsedRealtime mark as used time if non-zero
      * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
      *                with bucket values of ACTIVE and WORKING_SET.
      * @return
      */
     public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
-            int newBucket, long elapsedRealtime, long timeout) {
+            int newBucket, int usageReason, long elapsedRealtime, long timeout) {
         // Set the timeout if applicable
         if (timeout > elapsedRealtime) {
             // Convert to elapsed timebase
@@ -246,10 +251,10 @@
             if (DEBUG) {
                 Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
                         .currentBucket
-                        + ", reason=" + appUsageHistory.bucketingReason);
+                        + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
             }
         }
-        appUsageHistory.bucketingReason = REASON_USAGE;
+        appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
 
         return appUsageHistory;
     }
@@ -262,16 +267,17 @@
      * @param packageName
      * @param userId
      * @param newBucket the bucket to set the app to
-     * @param elapsedRealtime mark as used time if non-zero
+     * @param usageReason sub reason for usage
+     * @param nowElapsed mark as used time if non-zero
      * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
      *                with bucket values of ACTIVE and WORKING_SET.
      * @return
      */
     public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
-            long nowElapsed, long timeout) {
+            int usageReason, long nowElapsed, long timeout) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
-        return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
+        return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
     }
 
     private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -293,7 +299,7 @@
             appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
             appUsageHistory.lastPredictedTime = getElapsedTime(0);
             appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
-            appUsageHistory.bucketingReason = REASON_DEFAULT;
+            appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
             appUsageHistory.lastInformedBucket = -1;
             appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
             userHistory.put(packageName, appUsageHistory);
@@ -328,18 +334,18 @@
     }
 
     public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
-            int bucket, String reason) {
+            int bucket, int reason) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
                 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
         appUsageHistory.currentBucket = bucket;
         appUsageHistory.bucketingReason = reason;
-        if (reason.startsWith(REASON_PREDICTED)) {
+        if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
             appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
         }
         if (DEBUG) {
             Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
-                    + ", reason=" + appUsageHistory.bucketingReason);
+                    + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
         }
     }
 
@@ -393,11 +399,11 @@
         return buckets;
     }
 
-    public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
+    public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
                 getPackageHistory(userHistory, packageName, elapsedRealtime, false);
-        return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
+        return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
     }
 
     public long getElapsedTime(long elapsedRealtime) {
@@ -411,11 +417,11 @@
                 elapsedRealtime, true);
         if (idle) {
             appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
-            appUsageHistory.bucketingReason = REASON_FORCED;
+            appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
         } else {
             appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
             // This is to pretend that the app was just used, don't freeze the state anymore.
-            appUsageHistory.bucketingReason = REASON_USAGE;
+            appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
         }
         return appUsageHistory.currentBucket;
     }
@@ -516,7 +522,7 @@
                         appUsageHistory.currentBucket = currentBucketString == null
                                 ? STANDBY_BUCKET_ACTIVE
                                 : Integer.parseInt(currentBucketString);
-                        appUsageHistory.bucketingReason =
+                        String bucketingReason =
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                         appUsageHistory.lastJobRunTime = getLongValue(parser,
                                 ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
@@ -524,8 +530,13 @@
                                 ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
                         appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
                                 ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
-                        if (appUsageHistory.bucketingReason == null) {
-                            appUsageHistory.bucketingReason = REASON_DEFAULT;
+                        appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
+                        if (bucketingReason != null) {
+                            try {
+                                appUsageHistory.bucketingReason =
+                                        Integer.parseInt(bucketingReason, 16);
+                            } catch (NumberFormatException nfe) {
+                            }
                         }
                         appUsageHistory.lastInformedBucket = -1;
                         userHistory.put(packageName, appUsageHistory);
@@ -574,7 +585,8 @@
                         Long.toString(history.lastPredictedTime));
                 xml.attribute(null, ATTR_CURRENT_BUCKET,
                         Integer.toString(history.currentBucket));
-                xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
+                xml.attribute(null, ATTR_BUCKETING_REASON,
+                        Integer.toHexString(history.bucketingReason));
                 if (history.bucketActiveTimeoutTime > 0) {
                     xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
                             .bucketActiveTimeoutTime));
@@ -600,7 +612,7 @@
     }
 
     public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
-        idpw.println("Package idle stats:");
+        idpw.println("App Standby States:");
         idpw.increaseIndent();
         ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -615,24 +627,25 @@
                 continue;
             }
             idpw.print("package=" + packageName);
-            idpw.print(" userId=" + userId);
-            idpw.print(" lastUsedElapsed=");
+            idpw.print(" u=" + userId);
+            idpw.print(" bucket=" + appUsageHistory.currentBucket
+                    + " reason="
+                    + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
+            idpw.print(" used=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
-            idpw.print(" lastUsedScreenOn=");
+            idpw.print(" usedScr=");
             TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
-            idpw.print(" lastPredictedTime=");
+            idpw.print(" lastPred=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
-            idpw.print(" bucketActiveTimeoutTime=");
-            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
+            idpw.print(" activeLeft=");
+            TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
                     idpw);
-            idpw.print(" bucketWorkingSetTimeoutTime=");
-            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
+            idpw.print(" wsLeft=");
+            TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
                     idpw);
-            idpw.print(" lastJobRunTime=");
+            idpw.print(" lastJob=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
             idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
-            idpw.print(" bucket=" + appUsageHistory.currentBucket
-                    + " reason=" + appUsageHistory.bucketingReason);
             idpw.println();
         }
         idpw.println();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index f40aa5b..e836677 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,11 +16,20 @@
 
 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_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
 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;
@@ -129,8 +138,8 @@
             STANDBY_BUCKET_RARE
     };
 
-    // Expiration time for predicted bucket
-    private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+    /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
+    private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
 
     /**
      * Indicates the maximum wait time for admin data to be available;
@@ -187,6 +196,8 @@
     long mNotificationSeenTimeoutMillis;
     /** Minimum time a system update event should keep the buckets elevated. */
     long mSystemUpdateUsageTimeoutMillis;
+    /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
+    long mPredictionTimeoutMillis;
 
     volatile boolean mAppIdleEnabled;
     boolean mAppIdleTempParoled;
@@ -223,24 +234,30 @@
         // Whether the bucket change is because the user has started interacting with the app
         boolean isUserInteraction;
 
-        StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) {
+        // Reason for bucket change
+        int reason;
+
+        StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
+                boolean isInteraction) {
             this.packageName = pkgName;
             this.userId = userId;
             this.bucket = bucket;
+            this.reason = reason;
             this.isUserInteraction = isInteraction;
         }
 
         public static StandbyUpdateRecord obtain(String pkgName, int userId,
-                int bucket, boolean isInteraction) {
+                int bucket, int reason, boolean isInteraction) {
             synchronized (sStandbyUpdatePool) {
                 final int size = sStandbyUpdatePool.size();
                 if (size < 1) {
-                    return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction);
+                    return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
                 }
                 StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
                 r.packageName = pkgName;
                 r.userId = userId;
                 r.bucket = bucket;
+                r.reason = reason;
                 r.isUserInteraction = isInteraction;
                 return r;
             }
@@ -339,10 +356,11 @@
                 if (!packageName.equals(providerPkgName)) {
                     synchronized (mAppIdleLock) {
                         AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
-                                STANDBY_BUCKET_ACTIVE, elapsedRealtime,
+                                STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+                                elapsedRealtime,
                                 elapsedRealtime + mStrongUsageTimeoutMillis);
                         maybeInformListeners(packageName, userId, elapsedRealtime,
-                                appUsage.currentBucket, false);
+                                appUsage.currentBucket, appUsage.bucketingReason, false);
                     }
                 }
             } catch (PackageManager.NameNotFoundException e) {
@@ -497,50 +515,54 @@
         if (isSpecial) {
             synchronized (mAppIdleLock) {
                 mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
-                        STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+                        STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
             }
             maybeInformListeners(packageName, userId, elapsedRealtime,
-                    STANDBY_BUCKET_EXEMPTED, false);
+                    STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
         } else {
             synchronized (mAppIdleLock) {
                 final AppIdleHistory.AppUsageHistory app =
                         mAppIdleHistory.getAppUsageHistory(packageName,
                         userId, elapsedRealtime);
-                String reason = app.bucketingReason;
+                int reason = app.bucketingReason;
+                final int oldMainReason = reason & REASON_MAIN_MASK;
 
                 // If the bucket was forced by the user/developer, leave it alone.
                 // A usage event will be the only way to bring it out of this forced state
-                if (REASON_FORCED.equals(app.bucketingReason)) {
+                if (oldMainReason == REASON_MAIN_FORCED) {
                     return;
                 }
                 final int oldBucket = app.currentBucket;
                 int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
                 boolean predictionLate = false;
                 // Compute age-based bucket
-                if (REASON_DEFAULT.equals(app.bucketingReason)
-                        || REASON_USAGE.equals(app.bucketingReason)
-                        || REASON_TIMEOUT.equals(app.bucketingReason)
+                if (oldMainReason == REASON_MAIN_DEFAULT
+                        || oldMainReason == REASON_MAIN_USAGE
+                        || oldMainReason == REASON_MAIN_TIMEOUT
                         || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
                     newBucket = getBucketForLocked(packageName, userId,
                             elapsedRealtime);
                     if (DEBUG) {
                         Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
                     }
-                    reason = REASON_TIMEOUT;
+                    reason = REASON_MAIN_TIMEOUT;
                 }
                 // Check if the app is within one of the timeouts for forced bucket elevation
                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
                 if (newBucket >= STANDBY_BUCKET_ACTIVE
                         && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
                     newBucket = STANDBY_BUCKET_ACTIVE;
-                    reason = REASON_USAGE;
+                    reason = app.bucketingReason;
                     if (DEBUG) {
                         Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
                     }
                 } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
                         && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
                     newBucket = STANDBY_BUCKET_WORKING_SET;
-                    reason = REASON_USAGE;
+                    // If it was already there, keep the reason, else assume timeout to WS
+                    reason = (newBucket == oldBucket)
+                            ? app.bucketingReason
+                            : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
                     if (DEBUG) {
                         Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
                     }
@@ -553,43 +575,41 @@
                     mAppIdleHistory.setAppStandbyBucket(packageName, userId,
                             elapsedRealtime, newBucket, reason);
                     maybeInformListeners(packageName, userId, elapsedRealtime,
-                            newBucket, false);
+                            newBucket, reason, false);
                 }
             }
         }
     }
 
+    /** Returns true if there hasn't been a prediction for the app in a while. */
     private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
-        return app.bucketingReason != null
-                && app.bucketingReason.startsWith(REASON_PREDICTED)
+        return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
                 && app.lastPredictedTime > 0
                 && mAppIdleHistory.getElapsedTime(elapsedRealtime)
-                    - app.lastPredictedTime > PREDICTION_TIMEOUT;
+                    - app.lastPredictedTime > mPredictionTimeoutMillis;
     }
 
-    private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
-            long elapsedRealtime) {
-        final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
-        return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
-                && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
-    }
-
+    /** Inform listeners if the bucket has changed since it was last reported to listeners */
     private void maybeInformListeners(String packageName, int userId,
-            long elapsedRealtime, int bucket, boolean userStartedInteracting) {
+            long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
         synchronized (mAppIdleLock) {
-            // TODO: fold these into one call + lookup for efficiency if needed
             if (mAppIdleHistory.shouldInformListeners(packageName, userId,
                     elapsedRealtime, bucket)) {
-                StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
-                        bucket, userStartedInteracting);
+                final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
+                        bucket, reason, userStartedInteracting);
                 if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
-                        StandbyUpdateRecord.obtain(packageName, userId,
-                                bucket, userStartedInteracting)));
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
             }
         }
     }
 
+    /**
+     * Evaluates next bucket based on time since last used and the bucketing thresholds.
+     * @param packageName the app
+     * @param userId the user
+     * @param elapsedRealtime as the name suggests, current elapsed time
+     * @return the bucket for the app, based on time since last used
+     */
     @GuardedBy("mAppIdleLock")
     @StandbyBuckets int getBucketForLocked(String packageName, int userId,
             long elapsedRealtime) {
@@ -675,17 +695,20 @@
                 final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
                         event.mPackage, userId, elapsedRealtime);
                 final int prevBucket = appHistory.currentBucket;
-                final String prevBucketReason = appHistory.bucketingReason;
+                final int prevBucketReason = appHistory.bucketingReason;
                 final long nextCheckTime;
+                final int subReason = usageEventToSubReason(event.mEventType);
+                final int reason = REASON_MAIN_USAGE | subReason;
                 if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
                     // Mild usage elevates to WORKING_SET but doesn't change usage time.
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
-                            STANDBY_BUCKET_WORKING_SET,
+                            STANDBY_BUCKET_WORKING_SET, subReason,
                             0, elapsedRealtime + mNotificationSeenTimeoutMillis);
                     nextCheckTime = mNotificationSeenTimeoutMillis;
+
                 } else {
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
-                            STANDBY_BUCKET_ACTIVE,
+                            STANDBY_BUCKET_ACTIVE, subReason,
                             elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
                     nextCheckTime = mStrongUsageTimeoutMillis;
                 }
@@ -695,9 +718,9 @@
                 final boolean userStartedInteracting =
                         appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
                         prevBucket != appHistory.currentBucket &&
-                        prevBucketReason != REASON_USAGE;
+                        (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
                 maybeInformListeners(event.mPackage, userId, elapsedRealtime,
-                        appHistory.currentBucket, userStartedInteracting);
+                        appHistory.currentBucket, reason, userStartedInteracting);
 
                 if (previouslyIdle) {
                     notifyBatteryStats(event.mPackage, userId, false);
@@ -706,6 +729,17 @@
         }
     }
 
+    private int usageEventToSubReason(int eventType) {
+        switch (eventType) {
+            case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+            case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+            case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
+            case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
+            case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+            default: return 0;
+        }
+    }
+
     /**
      * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
      * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
@@ -731,7 +765,8 @@
                 userId, elapsedRealtime);
         // Inform listeners if necessary
         if (previouslyIdle != stillIdle) {
-            maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false);
+            maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
+                    REASON_MAIN_FORCED, false);
             if (!stillIdle) {
                 notifyBatteryStats(packageName, userId, idle);
             }
@@ -962,11 +997,11 @@
     }
 
     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
-            String reason, long elapsedRealtime) {
+            int reason, long elapsedRealtime) {
         synchronized (mAppIdleLock) {
             AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
                     userId, elapsedRealtime);
-            boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+            boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
 
             // Don't allow changing bucket if higher than ACTIVE
             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
@@ -979,7 +1014,7 @@
             }
 
             // If the bucket was forced, don't allow prediction to override
-            if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+            if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
 
             // If the bucket is required to stay in a higher state for a specified duration, don't
             // override unless the duration has passed
@@ -989,14 +1024,18 @@
                 if (newBucket > STANDBY_BUCKET_ACTIVE
                         && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
                     newBucket = STANDBY_BUCKET_ACTIVE;
-                    reason = REASON_USAGE;
+                    reason = app.bucketingReason;
                     if (DEBUG) {
                         Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
                     }
                 } else if (newBucket > STANDBY_BUCKET_WORKING_SET
                         && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
                     newBucket = STANDBY_BUCKET_WORKING_SET;
-                    reason = REASON_USAGE;
+                    if (app.currentBucket != newBucket) {
+                        reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+                    } else {
+                        reason = app.bucketingReason;
+                    }
                     if (DEBUG) {
                         Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
                     }
@@ -1006,7 +1045,7 @@
             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
                     reason);
         }
-        maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false);
+        maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
     }
 
     @VisibleForTesting
@@ -1106,11 +1145,12 @@
         return packageName != null && packageName.equals(activeScorer);
     }
 
-    void informListeners(String packageName, int userId, int bucket, boolean userInteraction) {
+    void informListeners(String packageName, int userId, int bucket, int reason,
+            boolean userInteraction) {
         final boolean idle = bucket >= STANDBY_BUCKET_RARE;
         synchronized (mPackageAccessListeners) {
             for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-                listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+                listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
                 if (userInteraction) {
                     listener.onUserInteractionStarted(packageName, userId);
                 }
@@ -1188,7 +1228,8 @@
                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
                     // Mark app as used for 2 hours. After that it can timeout to whatever the
                     // past usage pattern was.
-                    mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
+                    mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
+                            REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
                             elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
                 }
             }
@@ -1369,7 +1410,8 @@
             switch (msg.what) {
                 case MSG_INFORM_LISTENERS:
                     StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
-                    informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction);
+                    informListeners(r.packageName, r.userId, r.bucket, r.reason,
+                            r.isUserInteraction);
                     r.recycle();
                     break;
 
@@ -1477,7 +1519,7 @@
                 "notification_seen_duration";
         private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
                 "system_update_usage_duration";
-
+        private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -1547,6 +1589,9 @@
                 mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
                         (KEY_SYSTEM_UPDATE_HOLD_DURATION,
                                 COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
+                mPredictionTimeoutMillis = mParser.getDurationMillis
+                        (KEY_PREDICTION_TIMEOUT,
+                                COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
             }
         }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dedf967..43ac58a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -120,10 +120,10 @@
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
                 @Override
                 public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
-                        int bucket) {
+                        int bucket, int reason) {
                     Event event = new Event();
                     event.mEventType = Event.STANDBY_BUCKET_CHANGED;
-                    event.mBucket = bucket;
+                    event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
                     event.mPackage = packageName;
                     // This will later be converted to system time.
                     event.mTimeStamp = SystemClock.elapsedRealtime();
@@ -741,9 +741,9 @@
                 throw re.rethrowFromSystemServer();
             }
             final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
-            final String reason = shellCaller
-                    ? UsageStatsManager.REASON_FORCED
-                    : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+            final int reason = shellCaller
+                    ? UsageStatsManager.REASON_MAIN_FORCED
+                    : UsageStatsManager.REASON_MAIN_PREDICTED;
             final long token = Binder.clearCallingIdentity();
             try {
                 // Caller cannot set their own standby state
@@ -798,9 +798,9 @@
                 throw re.rethrowFromSystemServer();
             }
             final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
-            final String reason = shellCaller
-                    ? UsageStatsManager.REASON_FORCED
-                    : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+            final int reason = shellCaller
+                    ? UsageStatsManager.REASON_MAIN_FORCED
+                    : UsageStatsManager.REASON_MAIN_PREDICTED;
             final long token = Binder.clearCallingIdentity();
             try {
                 final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d1ed599..5e7e80d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,10 +26,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
-import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.Log;
-import android.util.LogWriter;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -175,7 +172,7 @@
                 event.mShortcutId = (id != null) ? id.intern() : null;
                 break;
             case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
-                event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+                event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
                 break;
         }
 
@@ -281,8 +278,8 @@
                 }
                 break;
             case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
-                if (event.mBucket != 0) {
-                    XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+                if (event.mBucketAndReason != 0) {
+                    XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
                 }
         }
 
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 8afc511..3fbcd81 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -520,7 +520,8 @@
             pw.printPair("shortcutId", event.mShortcutId);
         }
         if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
-            pw.printPair("standbyBucket", event.mBucket);
+            pw.printPair("standbyBucket", event.getStandbyBucket());
+            pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
         }
         pw.printHexPair("flags", event.mFlags);
         pw.println();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index b6be3c5..82a7450 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1215,6 +1216,7 @@
     }
 
     /** @hide */
+    @TestApi
     public void setSystemAndNetworkId(int systemId, int networkId) {
         this.mSystemId = systemId;
         this.mNetworkId = networkId;
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index c3f8a19..526733d 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -157,6 +157,13 @@
     }
 
     /**
+     * Returns the hex string of the certificate hash.
+     */
+    public String getCertificateHexString() {
+        return IccUtils.bytesToHexString(mCertificateHash);
+    }
+
+    /**
      * Returns the carrier privilege status associated with the given package.
      *
      * @param packageInfo package info fetched from
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2a13093..494ee65 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -83,6 +83,7 @@
     private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
     private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
     private static final String KEY_COMPILER_FILTERS = "compiler_filters";
+    private static final String KEY_FORCE_STOP_APP = "force_stop_app";
 
     private static final String SIMPLEPERF_APP_CMD =
             "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -103,6 +104,7 @@
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
     private static final String SUCCESS_MESSAGE = "Status: ok";
+    private static final String WARNING_MESSAGE = "Warning:";
     private static final String COMPILE_SUCCESS = "Success";
     private static final String THIS_TIME = "ThisTime:";
     private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
@@ -125,6 +127,7 @@
     private String mLaunchOrder = null;
     private boolean mDropCache = false;
     private int mLaunchIterations = 10;
+    private boolean mForceStopApp = true;
     private int mTraceLaunchCount = 0;
     private String mTraceDirectoryStr = null;
     private Bundle mResult = new Bundle();
@@ -246,7 +249,7 @@
                     // We only need to run a trial for the speed-profile filter, but we always
                     // run one for "applaunch.txt" consistency.
                     AppLaunchResult launchResult =
-                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                        startApp(launch.getApp(), launch.getLaunchReason());
                     if (launchResult.mLaunchTime < 0) {
                         addLaunchResult(launch, new AppLaunchResult());
                         // simply pass the app if launch isn't successful
@@ -274,7 +277,7 @@
                     }
                     // In the "applaunch.txt" file app launches are referenced using
                     // "LAUNCH_ITERATION - ITERATION NUM"
-                    launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
+                    launchResults = startApp(launch.getApp(), launch.getLaunchReason());
                     if (launchResults.mLaunchTime < 0) {
                         addLaunchResult(launch, new AppLaunchResult());
                         // if it fails once, skip the rest of the launches
@@ -295,14 +298,18 @@
                         atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
                                 traceDumpInterval, rootTraceSubDir,
                                 String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
-                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                        startApp(launch.getApp(), launch.getLaunchReason());
                         sleep(POST_LAUNCH_IDLE_TIMEOUT);
                     } finally {
                         // Stop the trace
                         atraceLogger.atraceStop();
                     }
                 }
-                closeApp(launch.getApp());
+                if(mForceStopApp) {
+                    closeApp(launch.getApp());
+                } else {
+                    startHomeIntent();
+                }
                 sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
             }
         } finally {
@@ -425,6 +432,10 @@
         if (launchIterations != null) {
             mLaunchIterations = Integer.parseInt(launchIterations);
         }
+        String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+        if (forceStopApp != null) {
+            mForceStopApp = Boolean.parseBoolean(forceStopApp);
+        }
         String appList = args.getString(KEY_APPS);
         if (appList == null)
             return;
@@ -522,8 +533,8 @@
         }
     }
 
-    private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
-            String launchReason) throws NameNotFoundException, RemoteException {
+    private AppLaunchResult startApp(String appName, String launchReason)
+            throws NameNotFoundException, RemoteException {
         Log.i(TAG, "Starting " + appName);
 
         Intent startIntent = mNameToIntent.get(appName);
@@ -532,8 +543,7 @@
             mResult.putString(mNameToResultKey.get(appName), "App does not exist");
             return new AppLaunchResult();
         }
-        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
-                launchReason);
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, launchReason);
         Thread t = new Thread(runnable);
         t.start();
         try {
@@ -682,13 +692,10 @@
     private class AppLaunchRunnable implements Runnable {
         private Intent mLaunchIntent;
         private AppLaunchResult mLaunchResult;
-        private boolean mForceStopBeforeLaunch;
         private String mLaunchReason;
 
-        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
-                String launchReason) {
+        public AppLaunchRunnable(Intent intent, String launchReason) {
             mLaunchIntent = intent;
-            mForceStopBeforeLaunch = forceStopBeforeLaunch;
             mLaunchReason = launchReason;
             mLaunchResult = new AppLaunchResult();
         }
@@ -702,7 +709,7 @@
             try {
                 String packageName = mLaunchIntent.getComponent().getPackageName();
                 String componentName = mLaunchIntent.getComponent().flattenToShortString();
-                if (mForceStopBeforeLaunch) {
+                if (mForceStopApp) {
                     mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
                 }
                 String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
@@ -752,10 +759,11 @@
             String launchTime = "-1";
             String cpuCycles = "-1";
             String majorFaults = "-1";
-            boolean launchSuccess = false;
+            boolean coldLaunchSuccess = false;
+            boolean hotLaunchSuccess = false;
             try {
                 InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
-                /* SAMPLE OUTPUT :
+                /* SAMPLE OUTPUT : Cold launch
                 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
                 Status: ok
                 Activity: com.google.android.calculator/com.android.calculator2.Calculator
@@ -763,6 +771,15 @@
                 TotalTime: 357
                 WaitTime: 377
                 Complete*/
+                /* SAMPLE OUTPUT : Hot launch
+                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
+                Warning: Activity not started, its current task has been brought to the front
+                Status: ok
+                Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
+                ThisTime: 60
+                TotalTime: 60
+                WaitTime: 67
+                Complete*/
                 /* WITH SIMPLEPERF :
                 Performance counter statistics,
                 6595722690,cpu-cycles,4.511040,GHz,(100%),
@@ -776,24 +793,33 @@
                 mBufferedWriter.write(headerInfo);
                 mBufferedWriter.newLine();
                 while ((line = bufferedReader.readLine()) != null) {
-                    if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
-                        launchSuccess = true;
+                    if (lineCount == 2 && line.startsWith(SUCCESS_MESSAGE)) {
+                        coldLaunchSuccess = true;
+                    }
+                    if (lineCount == 2 && line.startsWith(WARNING_MESSAGE)) {
+                        hotLaunchSuccess = true;
                     }
                     // Parse TotalTime which is the launch time
-                    if (launchSuccess && lineCount == 5) {
+                    if (coldLaunchSuccess && lineCount == 5) {
+                        String launchSplit[] = line.split(":");
+                        launchTime = launchSplit[1].trim();
+                    }
+                    if (hotLaunchSuccess && lineCount == 6) {
                         String launchSplit[] = line.split(":");
                         launchTime = launchSplit[1].trim();
                     }
 
                     if (mSimplePerfAppOnly) {
                         // Parse simpleperf output.
-                        if (lineCount == 9) {
+                        if ((lineCount == 9 && coldLaunchSuccess)
+                                || (lineCount == 10 && hotLaunchSuccess)) {
                             if (!line.contains("cpu-cycles")) {
                                 Log.e(TAG, "Error in simpleperf output");
                             } else {
                                 cpuCycles = line.split(",")[0].trim();
                             }
-                        } else if (lineCount == 10) {
+                        } else if ((lineCount == 10 && coldLaunchSuccess)
+                                || (lineCount == 11 && hotLaunchSuccess)) {
                             if (!line.contains("major-faults")) {
                                 Log.e(TAG, "Error in simpleperf output");
                             } else {
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 388f91a..261ea2e 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -66,9 +66,6 @@
         super(SurfaceCompositionMeasuringActivity.class);
     }
 
-    private void testRestoreContexts() {
-    }
-
     @SmallTest
     public void testSurfaceCompositionPerformance() {
         Bundle status = new Bundle();