Merge "Import translations. DO NOT MERGE" into pi-dev
diff --git a/api/current.txt b/api/current.txt
index 398fc3f..9c0e630 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -42821,17 +42821,17 @@
     ctor public ServiceState(android.os.Parcel);
     method protected void copyFrom(android.telephony.ServiceState);
     method public int describeContents();
+    method public int getCdmaNetworkId();
+    method public int getCdmaSystemId();
     method public int[] getCellBandwidths();
     method public int getChannelNumber();
     method public int getDuplexMode();
     method public boolean getIsManualSelection();
-    method public int getNetworkId();
     method public java.lang.String getOperatorAlphaLong();
     method public java.lang.String getOperatorAlphaShort();
     method public java.lang.String getOperatorNumeric();
     method public boolean getRoaming();
     method public int getState();
-    method public int getSystemId();
     method public void setIsManualSelection(boolean);
     method public void setOperatorName(java.lang.String, java.lang.String, java.lang.String);
     method public void setRoaming(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index af783ca..1080669 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -729,9 +729,14 @@
   public final class UsageStatsManager {
     method public int getAppStandbyBucket(java.lang.String);
     method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
+    method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
+    method public void unregisterAppUsageObserver(int);
     method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
+    field public static final java.lang.String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
+    field public static final java.lang.String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
+    field public static final java.lang.String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
     field public static final int STANDBY_BUCKET_EXEMPTED = 5; // 0x5
     field public static final int STANDBY_BUCKET_NEVER = 50; // 0x32
   }
@@ -4327,11 +4332,14 @@
     method public deprecated 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 java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
+    method public android.security.keystore.recovery.KeyChainSnapshot getKeyChainSnapshot() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int[] getRecoverySecretTypes() 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 int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
     method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void initRecoveryService(java.lang.String, byte[], 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;
diff --git a/api/test-current.txt b/api/test-current.txt
index 54ddd5d..3ddbdba 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -759,7 +759,7 @@
   }
 
   public class ServiceState implements android.os.Parcelable {
-    method public void setSystemAndNetworkId(int, int);
+    method public void setCdmaSystemAndNetworkId(int, int);
   }
 
 }
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index fff1a00..d52bd37 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -16,6 +16,7 @@
 
 package android.app.usage;
 
+import android.app.PendingIntent;
 import android.app.usage.UsageEvents;
 import android.content.pm.ParceledListSlice;
 
@@ -43,4 +44,7 @@
     void setAppStandbyBucket(String packageName, int bucket, int userId);
     ParceledListSlice getAppStandbyBuckets(String callingPackage, int userId);
     void setAppStandbyBuckets(in ParceledListSlice appBuckets, int userId);
+    void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
+            in PendingIntent callback, String callingPackage);
+    void unregisterAppUsageObserver(int observerId, String callingPackage);
 }
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 5f9fa43..59f001c 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -20,6 +20,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
@@ -32,6 +33,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Provides access to device usage history and statistics. Usage data is aggregated into
@@ -179,6 +181,31 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandbyBuckets {}
 
+    /**
+     * Observer id of the registered observer for the group of packages that reached the usage
+     * time limit. Included as an extra in the PendingIntent that was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_OBSERVER_ID = "android.app.usage.extra.OBSERVER_ID";
+
+    /**
+     * Original time limit in milliseconds specified by the registered observer for the group of
+     * packages that reached the usage time limit. Included as an extra in the PendingIntent that
+     * was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_TIME_LIMIT = "android.app.usage.extra.TIME_LIMIT";
+
+    /**
+     * Actual usage time in milliseconds for the group of packages that reached the specified time
+     * limit. Included as an extra in the PendingIntent that was registered.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_TIME_USED = "android.app.usage.extra.TIME_USED";
+
     private static final UsageEvents sEmptyResults = new UsageEvents();
 
     private final Context mContext;
@@ -470,6 +497,53 @@
         }
     }
 
+    /**
+     * @hide
+     * Register an app usage limit observer that receives a callback on the provided intent when
+     * the sum of usages of apps in the packages array exceeds the timeLimit specified. The
+     * observer will automatically be unregistered when the time limit is reached and the intent
+     * is delivered.
+     * @param observerId A unique id associated with the group of apps to be monitored. There can
+     *                  be multiple groups with common packages and different time limits.
+     * @param packages The list of packages to observe for foreground activity time. Must include
+     *                 at least one package.
+     * @param timeLimit The total time the set of apps can be in the foreground before the
+     *                  callbackIntent is delivered. Must be greater than 0.
+     * @param timeUnit The unit for time specified in timeLimit.
+     * @param callbackIntent The PendingIntent that will be dispatched when the time limit is
+     *                       exceeded by the group of apps. The delivered Intent will also contain
+     *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
+     *                       {@link #EXTRA_TIME_USED}.
+     * @throws SecurityException if the caller doesn't have the PACKAGE_USAGE_STATS permission.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    public void registerAppUsageObserver(int observerId, String[] packages, long timeLimit,
+            TimeUnit timeUnit, PendingIntent callbackIntent) {
+        try {
+            mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
+                    callbackIntent, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * @hide
+     * Unregister the app usage observer specified by the observerId. This will only apply to any
+     * observer registered by this application. Unregistering an observer that was already
+     * unregistered or never registered will have no effect.
+     * @param observerId The id of the observer that was previously registered.
+     * @throws SecurityException if the caller doesn't have the PACKAGE_USAGE_STATS permission.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    public void unregisterAppUsageObserver(int observerId) {
+        try {
+            mService.unregisterAppUsageObserver(observerId, mContext.getOpPackageName());
+        } catch (RemoteException e) {
+        }
+    }
+
     /** @hide */
     public static String reasonToString(int standbyReason) {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 04cceb8..848c596 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -331,7 +331,6 @@
         final long looperToken = proto.start(fieldId);
         proto.write(LooperProto.THREAD_NAME, mThread.getName());
         proto.write(LooperProto.THREAD_ID, mThread.getId());
-        proto.write(LooperProto.IDENTITY_HASH_CODE, System.identityHashCode(this));
         mQueue.writeToProto(proto, LooperProto.QUEUE);
         proto.end(looperToken);
     }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 97d415e..66fa629 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1643,12 +1643,12 @@
         public void writeToProto(ProtoOutputStream proto, long fieldId) {
             synchronized (mToken) {
                 final long token = proto.start(fieldId);
-                proto.write(PowerManagerProto.WakeLockProto.HEX_STRING,
-                        Integer.toHexString(System.identityHashCode(this)));
-                proto.write(PowerManagerProto.WakeLockProto.HELD, mHeld);
-                proto.write(PowerManagerProto.WakeLockProto.INTERNAL_COUNT, mInternalCount);
+                proto.write(PowerManagerProto.WakeLock.TAG, mTag);
+                proto.write(PowerManagerProto.WakeLock.PACKAGE_NAME, mPackageName);
+                proto.write(PowerManagerProto.WakeLock.HELD, mHeld);
+                proto.write(PowerManagerProto.WakeLock.INTERNAL_COUNT, mInternalCount);
                 if (mWorkSource != null) {
-                    mWorkSource.writeToProto(proto, PowerManagerProto.WakeLockProto.WORK_SOURCE);
+                    mWorkSource.writeToProto(proto, PowerManagerProto.WakeLock.WORK_SOURCE);
                 }
                 proto.end(token);
             }
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index aa09f10..3d3b6d5 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -215,8 +215,8 @@
 
         /**
          * Creates a new {@link KeyChainProtectionParams} instance.
-         * The instance will include default values, if {@link setSecret}
-         * or {@link setUserSecretType} were not called.
+         * The instance will include default values, if {@link #setSecret}
+         * or {@link #setUserSecretType} were not called.
          *
          * @return new instance
          * @throws NullPointerException if some required fields were not set.
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index fc909a0..ef5e90c 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -30,7 +30,7 @@
 
 /**
  * Collection of parameters which define a key derivation function.
- * Currently only supports salted SHA-256
+ * Currently only supports salted SHA-256.
  *
  * @hide
  */
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 4881375..7523afd 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -33,6 +33,7 @@
 
 import java.security.Key;
 import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.List;
@@ -156,6 +157,7 @@
     /**
      * Gets a new instance of the class.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public static RecoveryController getInstance(Context context) {
         ILockSettings lockSettings =
                 ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
@@ -245,8 +247,6 @@
      * @return Data necessary to recover keystore or {@code null} if snapshot is not available.
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
-     *
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @Nullable KeyChainSnapshot getKeyChainSnapshot()
@@ -288,7 +288,7 @@
     /**
      * Server parameters used to generate new recovery key blobs. This value will be included in
      * {@code KeyChainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
-     * in vaultParams {@link #startRecoverySession}
+     * in vaultParams {@link RecoverySession#start(CertPath, byte[], byte[], List)}.
      *
      * @param serverParams included in recovery key blob.
      * @see #getRecoveryData
@@ -310,6 +310,7 @@
      * @deprecated Use {@link #getAliases()}.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public List<String> getAliases(@Nullable String packageName)
             throws InternalRecoveryServiceException {
         return getAliases();
@@ -318,6 +319,7 @@
     /**
      * Returns a list of aliases of keys belonging to the application.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public List<String> getAliases() throws InternalRecoveryServiceException {
         try {
             Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
@@ -367,6 +369,7 @@
      * @deprecated Use {@link #getRecoveryStatus(String)}.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public int getRecoveryStatus(String packageName, String alias)
             throws InternalRecoveryServiceException {
         return getRecoveryStatus(alias);
@@ -385,6 +388,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
         try {
             Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
@@ -410,6 +414,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void setRecoverySecretTypes(
             @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
             throws InternalRecoveryServiceException {
@@ -431,6 +436,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @NonNull @KeyChainProtectionParams.UserSecretType int[] getRecoverySecretTypes()
             throws InternalRecoveryServiceException {
         try {
@@ -452,6 +458,7 @@
      *     service.
      */
     @NonNull
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
             throws InternalRecoveryServiceException {
         try {
@@ -474,6 +481,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
             throws InternalRecoveryServiceException {
         try {
@@ -498,6 +506,7 @@
      *     to generate recoverable keys, as the snapshots are encrypted using a key derived from the
      *     lock screen.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
         try {
@@ -512,11 +521,11 @@
         }
     }
 
-    // TODO: Unhide the following APIs, generateKey(), importKey(), and getKey()
     /**
      * @deprecated Use {@link #generateKey(String)}.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public Key generateKey(@NonNull String alias, byte[] account)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
         return generateKey(alias);
@@ -530,6 +539,7 @@
      * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
      *     screen is required to generate recoverable keys.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public Key generateKey(@NonNull String alias) throws InternalRecoveryServiceException,
             LockScreenRequiredException {
         try {
@@ -562,8 +572,8 @@
      * @throws LockScreenRequiredException if the user does not have a lock screen set. A lock
      *     screen is required to generate recoverable keys.
      *
-     * @hide
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public Key importKey(@NonNull String alias, byte[] keyBytes)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
         try {
@@ -595,8 +605,8 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      * @throws UnrecoverableKeyException if key is permanently invalidated or not found.
-     * @hide
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public @Nullable Key getKey(@NonNull String alias)
             throws InternalRecoveryServiceException, UnrecoverableKeyException {
         try {
@@ -622,6 +632,7 @@
      * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
      *     service.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException {
         try {
             mBinder.removeKey(alias);
@@ -637,6 +648,7 @@
      *
      * <p>A recovery session is required to restore keys from a remote store.
      */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public RecoverySession createRecoverySession() {
         return RecoverySession.newInstance(this);
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d908e79..d521684 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -29,6 +29,7 @@
 import android.Manifest;
 import android.animation.LayoutTransition;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ResourcesManager;
@@ -4152,9 +4153,7 @@
                         Log.v(TAG, "Dispatching key " + msg.obj + " from Autofill to " + mView);
                     }
                     KeyEvent event = (KeyEvent) msg.obj;
-                    // send InputEvent to pre IME, set FLAG_FROM_AUTOFILL so the InputEvent
-                    // wont be dropped as app window is not focus.
-                    enqueueInputEvent(event, null, QueuedInputEvent.FLAG_FROM_AUTOFILL, true);
+                    enqueueInputEvent(event, null, 0, true);
                 } break;
                 case MSG_CHECK_FOCUS: {
                     InputMethodManager imm = InputMethodManager.peekInstance();
@@ -4447,7 +4446,7 @@
                 return true;
             } else if ((!mAttachInfo.mHasWindowFocus
                     && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
-                    && (q.mFlags & QueuedInputEvent.FLAG_FROM_AUTOFILL) == 0) || mStopped
+                    && !isAutofillUiShowing()) || mStopped
                     || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
                     || (mPausedForTransition && !isBack(q.mEvent))) {
                 // This is a focus event and the window doesn't currently have input focus or
@@ -4782,18 +4781,11 @@
                 ensureTouchMode(event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN));
             }
 
-            if (action == MotionEvent.ACTION_DOWN && mView instanceof ViewGroup) {
+            if (action == MotionEvent.ACTION_DOWN) {
                 // Upon motion event within app window, close autofill ui.
-                ViewGroup decorView = (ViewGroup) mView;
-                if (decorView.getChildCount() > 0) {
-                    // We cannot use decorView's Context for querying AutofillManager: DecorView's
-                    // context is based on Application Context, it would allocate a different
-                    // AutofillManager instance.
-                    AutofillManager afm = (AutofillManager) decorView.getChildAt(0).getContext()
-                            .getSystemService(Context.AUTOFILL_MANAGER_SERVICE);
-                    if (afm != null) {
-                        afm.requestHideFillUi();
-                    }
+                AutofillManager afm = getAutofillManager();
+                if (afm != null) {
+                    afm.requestHideFillUi();
                 }
             }
 
@@ -6435,6 +6427,28 @@
         return mAudioManager;
     }
 
+    private @Nullable AutofillManager getAutofillManager() {
+        if (mView instanceof ViewGroup) {
+            ViewGroup decorView = (ViewGroup) mView;
+            if (decorView.getChildCount() > 0) {
+                // We cannot use decorView's Context for querying AutofillManager: DecorView's
+                // context is based on Application Context, it would allocate a different
+                // AutofillManager instance.
+                return decorView.getChildAt(0).getContext()
+                        .getSystemService(AutofillManager.class);
+            }
+        }
+        return null;
+    }
+
+    private boolean isAutofillUiShowing() {
+        AutofillManager afm = getAutofillManager();
+        if (afm == null) {
+            return false;
+        }
+        return afm.isAutofillUiShowing();
+    }
+
     public AccessibilityInteractionController getAccessibilityInteractionController() {
         if (mView == null) {
             throw new IllegalStateException("getAccessibilityInteractionController"
@@ -6840,7 +6854,6 @@
         public static final int FLAG_FINISHED_HANDLED = 1 << 3;
         public static final int FLAG_RESYNTHESIZED = 1 << 4;
         public static final int FLAG_UNHANDLED = 1 << 5;
-        public static final int FLAG_FROM_AUTOFILL = 1 << 6;
 
         public QueuedInputEvent mNext;
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 158c2ee0b..c109297 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1407,6 +1407,15 @@
         return client;
     }
 
+    /**
+     * Check if autofill ui is showing, must be called on UI thread.
+     * @hide
+     */
+    public boolean isAutofillUiShowing() {
+        final AutofillClient client = mContext.getAutofillClient();
+        return client != null & client.autofillClientIsFillUiShowing();
+    }
+
     /** @hide */
     public void onAuthenticationResult(int authenticationId, Intent data, View focusView) {
         if (!hasAutofillFeature()) {
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index 74b47d2..6a174e8 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -32,7 +32,7 @@
 
   optional float font_scale = 1;
   optional uint32 mcc = 2;
-  optional uint32 mnc = 3;
+  optional uint32 mnc = 3 [ (.android.privacy).dest = DEST_EXPLICIT ];
   repeated LocaleProto locales = 4;
   optional uint32 screen_layout = 5;
   optional uint32 color_mode = 6;
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 5e0ed11..3b2c4fc 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -59,7 +59,7 @@
     optional ComponentNameProto component = 7;
     optional string source_bounds = 8;
     optional string clip_data = 9 [ (.android.privacy).dest = DEST_EXPLICIT ];
-    optional string extras = 10 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    optional string extras = 10 [ (.android.privacy).dest = DEST_LOCAL ];
     optional int32 content_user_hint = 11;
     optional string selector = 12;
 }
diff --git a/core/proto/android/os/looper.proto b/core/proto/android/os/looper.proto
index 435c648..dce65d3 100644
--- a/core/proto/android/os/looper.proto
+++ b/core/proto/android/os/looper.proto
@@ -25,8 +25,8 @@
 message LooperProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional string thread_name = 1 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    // the thread name, usually set by developers.
+    optional string thread_name = 1;
     optional int64 thread_id = 2;
-    optional int32 identity_hash_code = 3;
-    optional android.os.MessageQueueProto queue = 4;
+    optional android.os.MessageQueueProto queue = 3;
 }
diff --git a/core/proto/android/os/powermanager.proto b/core/proto/android/os/powermanager.proto
index 78a28ed..20b0a74 100644
--- a/core/proto/android/os/powermanager.proto
+++ b/core/proto/android/os/powermanager.proto
@@ -36,13 +36,14 @@
     }
 
     // WakeLock class in android.os.PowerManager, it is the one used by sdk
-    message WakeLockProto {
+    message WakeLock {
         option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-        optional string hex_string = 1;
-        optional bool held = 2;
-        optional int32 internal_count = 3;
-        optional WorkSourceProto work_source = 4;
+        optional string tag = 1;
+        optional string package_name = 2;
+        optional bool held = 3;
+        optional int32 internal_count = 4;
+        optional WorkSourceProto work_source = 5;
     }
 }
 
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 3b9150f..5491ca5 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -157,7 +157,7 @@
   optional BroadcastRecordProto current = 5;
   optional bool linked_to_death = 6;
   repeated BroadcastFilterProto filters = 7;
-  optional string hex_hash = 8; // this hash is used to find the object in IntentResolver
+  optional string hex_hash = 8; // used to find this ReceiverList object in IntentResolver
 }
 
 message ProcessRecordProto {
@@ -184,7 +184,7 @@
 
   optional .android.content.IntentFilterProto intent_filter = 1;
   optional string required_permission = 2;
-  optional string hex_hash = 3; // used to find the object in IntentResolver
+  optional string hex_hash = 3; // used to find the BroadcastFilter object in IntentResolver
   optional int32 owning_user_id = 4;
 }
 
@@ -458,13 +458,12 @@
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
   optional string short_name = 1;
-  optional string hex_hash = 2;
-  optional bool is_running = 3; // false if the application service is null
-  optional int32 pid = 4;
-  optional .android.content.IntentProto intent = 5;
-  optional string package_name = 6;
-  optional string process_name = 7;
-  optional string permission = 8;
+  optional bool is_running = 2; // false if the application service is null
+  optional int32 pid = 3;
+  optional .android.content.IntentProto intent = 4;
+  optional string package_name = 5;
+  optional string process_name = 6;
+  optional string permission = 7;
 
   message AppInfo {
     option (.android.msg_privacy).dest = DEST_EXPLICIT;
@@ -473,11 +472,11 @@
     optional string res_dir = 2;
     optional string data_dir = 3;
   }
-  optional AppInfo appinfo = 9;
-  optional ProcessRecordProto app = 10;
-  optional ProcessRecordProto isolated_proc = 11;
-  optional bool whitelist_manager = 12;
-  optional bool delayed = 13;
+  optional AppInfo appinfo = 8;
+  optional ProcessRecordProto app = 9;
+  optional ProcessRecordProto isolated_proc = 10;
+  optional bool whitelist_manager = 11;
+  optional bool delayed = 12;
 
   message Foreground {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -485,13 +484,13 @@
     optional int32 id = 1;
     optional .android.app.NotificationProto notification = 2;
   }
-  optional Foreground foreground = 14;
+  optional Foreground foreground = 13;
 
-  optional .android.util.Duration create_real_time = 15;
-  optional .android.util.Duration starting_bg_timeout = 16;
-  optional .android.util.Duration last_activity_time = 17;
-  optional .android.util.Duration restart_time = 18;
-  optional bool created_from_fg = 19;
+  optional .android.util.Duration create_real_time = 14;
+  optional .android.util.Duration starting_bg_timeout = 15;
+  optional .android.util.Duration last_activity_time = 16;
+  optional .android.util.Duration restart_time = 17;
+  optional bool created_from_fg = 18;
 
   // variables used to track states related to service start
   message Start {
@@ -503,7 +502,7 @@
     optional bool call_start = 4;
     optional int32 last_start_id = 5;
   }
-  optional Start start = 20;
+  optional Start start = 19;
 
   message ExecuteNesting {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -512,9 +511,9 @@
     optional bool execute_fg = 2;
     optional .android.util.Duration executing_start = 3;
   }
-  optional ExecuteNesting execute = 21;
+  optional ExecuteNesting execute = 20;
 
-  optional .android.util.Duration destory_time = 22;
+  optional .android.util.Duration destory_time = 21;
 
   message Crash {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -524,9 +523,9 @@
     optional .android.util.Duration next_restart_time = 3;
     optional int32 crash_count = 4;
   }
-  optional Crash crash = 23;
+  optional Crash crash = 22;
 
-  message StartItemProto {
+  message StartItem {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional int32 id = 1;
@@ -537,17 +536,20 @@
     optional NeededUriGrantsProto needed_grants = 6;
     optional UriPermissionOwnerProto uri_permissions = 7;
   }
-  repeated StartItemProto delivered_starts = 24;
-  repeated StartItemProto pending_starts = 25;
+  repeated StartItem delivered_starts = 23;
+  repeated StartItem pending_starts = 24;
 
-  repeated IntentBindRecordProto bindings = 26;
-  repeated ConnectionRecordProto connections = 27;
+  repeated IntentBindRecordProto bindings = 25;
+  repeated ConnectionRecordProto connections = 26;
+
+  // Next Tag: 27
 }
 
 message ConnectionRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional string hex_hash = 1;
+  // used to find same record, e.g. AppBindRecord has the hex_hash
+  optional string hex_hash = 1; // cross reference the object and avoid double logging.
   optional int32 user_id = 2;
 
   enum Flag {
@@ -570,30 +572,28 @@
   }
   repeated Flag flags = 3;
   optional string service_name = 4;
-  optional string conn_hex_hash = 5;
 }
 
 message AppBindRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional string hex_hash = 1;
-  optional ProcessRecordProto client = 2;
-  repeated ConnectionRecordProto connections = 3;
+  optional string service_name = 1;
+  optional string client_proc_name = 2;
+  repeated string connections = 3; // hex_hash of ConnectionRecordProto
 }
 
 message IntentBindRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional string hex_hash = 1;
-  optional bool is_create = 2;
-  optional .android.content.IntentProto intent = 3;
-  optional string binder = 4;
-  optional bool requested = 5;
-  optional bool received = 6;
-  optional bool has_bound = 7;
-  optional bool do_rebind = 8;
+  optional .android.content.IntentProto intent = 1;
+  optional string binder = 2;
+  optional bool auto_create = 3; // value of BIND_AUTO_CREATE flag.
+  optional bool requested = 4;
+  optional bool received = 5;
+  optional bool has_bound = 6;
+  optional bool do_rebind = 7;
 
-  repeated AppBindRecordProto apps = 9;
+  repeated AppBindRecordProto apps = 8;
 }
 
 // TODO: "dumpsys activity --proto processes"
@@ -688,10 +688,10 @@
   optional SleepStatus sleep_status = 27;
 
   message VoiceProto {
-    option (.android.msg_privacy).dest = DEST_EXPLICIT;
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional string session = 1;
-    optional .android.os.PowerManagerProto.WakeLockProto wakelock = 2;
+    optional .android.os.PowerManagerProto.WakeLock wakelock = 2;
   }
   optional VoiceProto running_voice = 28;
 
@@ -780,8 +780,8 @@
   optional bool call_finish_booting = 44;
   optional bool boot_animation_complete = 45;
   optional int64 last_power_check_uptime_ms = 46;
-  optional .android.os.PowerManagerProto.WakeLockProto going_to_sleep = 47;
-  optional .android.os.PowerManagerProto.WakeLockProto launching_activity = 48;
+  optional .android.os.PowerManagerProto.WakeLock going_to_sleep = 47;
+  optional .android.os.PowerManagerProto.WakeLock launching_activity = 48;
   optional int32 adj_seq = 49;
   optional int32 lru_seq = 50;
   optional int32 num_non_cached_procs = 51;
@@ -813,14 +813,13 @@
 message UidRecordProto {
   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-  optional string hex_hash = 1;
-  optional int32 uid = 2;
-  optional .android.app.ProcessStateEnum current = 3;
-  optional bool ephemeral = 4;
-  optional bool fg_services = 5;
-  optional bool whilelist = 6;
-  optional .android.util.Duration last_background_time = 7;
-  optional bool idle = 8;
+  optional int32 uid = 1;
+  optional .android.app.ProcessStateEnum current = 2;
+  optional bool ephemeral = 3;
+  optional bool fg_services = 4;
+  optional bool whilelist = 5;
+  optional .android.util.Duration last_background_time = 6;
+  optional bool idle = 7;
 
   enum Change {
     CHANGE_GONE = 0;
@@ -829,8 +828,8 @@
     CHANGE_CACHED = 3;
     CHANGE_UNCACHED = 4;
   }
-  repeated Change last_reported_changes = 9;
-  optional int32 num_procs = 10;
+  repeated Change last_reported_changes = 8;
+  optional int32 num_procs = 9;
 
   message ProcStateSequence {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -839,7 +838,9 @@
     optional int64 last_network_updated = 2;
     optional int64 last_dispatched = 3;
   }
-  optional ProcStateSequence network_state_update = 11;
+  optional ProcStateSequence network_state_update = 10;
+
+  // Next Tag: 11
 }
 
 // proto of class ImportanceToken in ActivityManagerService
diff --git a/core/proto/android/server/intentresolver.proto b/core/proto/android/server/intentresolver.proto
index 0ada895..e67723e 100644
--- a/core/proto/android/server/intentresolver.proto
+++ b/core/proto/android/server/intentresolver.proto
@@ -24,9 +24,9 @@
 message IntentResolverProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-
+    // A mapping bewteen some key string to IntentFilter's toString().
     message ArrayMapEntry {
-        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+        option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
         optional string key = 1;
         repeated string values = 2;
diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto
index e5bdc8b..f55f0e7 100644
--- a/core/proto/android/service/diskstats.proto
+++ b/core/proto/android/service/diskstats.proto
@@ -37,8 +37,8 @@
     }
     // Whether the latency test resulted in an error
     optional bool has_test_error = 1;
-    // If the test errored, error message is contained here
-    optional string error_message = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+    // If the test errored, error message is contained here, it is just IOException.
+    optional string error_message = 2;
     // 512B write latency in milliseconds, if the test was successful
     optional int32 write_512b_latency_millis = 3;
     // Free Space in the major partitions
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index e3be5d4..62d941a 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5507,6 +5507,10 @@
     // internal platform metrics use.
     RESERVED_FOR_LOGBUILDER_LATENCY_MILLIS = 1359;
 
+    // OPEN: Settings > Gestures > Prevent Ringing
+    // OS: P
+    SETTINGS_PREVENT_RINGING = 1360;
+
     // ---- 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/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dd31a84..04d46aa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -25932,16 +25932,19 @@
                 Bundle bOptions) {
             Preconditions.checkNotNull(intents, "intents");
             final String[] resolvedTypes = new String[intents.length];
-            for (int i = 0; i < intents.length; i++) {
-                resolvedTypes[i] = intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
-            }
 
             // UID of the package on user userId.
             // "= 0" is needed because otherwise catch(RemoteException) would make it look like
             // packageUid may not be initialized.
             int packageUid = 0;
             final long ident = Binder.clearCallingIdentity();
+
             try {
+                for (int i = 0; i < intents.length; i++) {
+                    resolvedTypes[i] =
+                            intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+                }
+
                 packageUid = AppGlobals.getPackageManager().getPackageUid(
                         packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a24f84a..8f987be 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -5287,6 +5287,14 @@
 
     boolean shouldSleepActivities() {
         final ActivityDisplay display = getDisplay();
+
+        // Do not sleep activities in this stack if we're marked as focused and the keyguard
+        // is in the process of going away.
+        if (mStackSupervisor.getFocusedStack() == this
+                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+            return false;
+        }
+
         return display != null ? display.isSleeping() : mService.isSleepingLocked();
     }
 
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index 7b38597..972a692 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -66,14 +66,13 @@
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(AppBindRecordProto.HEX_HASH,
-                Integer.toHexString(System.identityHashCode(this)));
-        if (client != null) {
-            client.writeToProto(proto, AppBindRecordProto.CLIENT);
-        }
+        proto.write(AppBindRecordProto.SERVICE_NAME, service.shortName);
+        proto.write(AppBindRecordProto.CLIENT_PROC_NAME, client.processName);
         final int N = connections.size();
         for (int i=0; i<N; i++) {
-            connections.valueAt(i).writeToProto(proto, AppBindRecordProto.CONNECTIONS);
+            ConnectionRecord conn = connections.valueAt(i);
+            proto.write(AppBindRecordProto.CONNECTIONS,
+                Integer.toHexString(System.identityHashCode(conn)));
         }
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index d320fb1..a8e9ad9 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -176,10 +176,6 @@
         if (binding.service != null) {
             proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
         }
-        if (conn != null) {
-            proto.write(ConnectionRecordProto.CONN_HEX_HASH,
-                    Integer.toHexString(System.identityHashCode(conn.asBinder())));
-        }
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index 01ce64c..3457a80 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -113,10 +113,6 @@
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(IntentBindRecordProto.HEX_HASH,
-                Integer.toHexString(System.identityHashCode(this)));
-        proto.write(IntentBindRecordProto.IS_CREATE,
-                (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
         if (intent != null) {
             intent.getIntent().writeToProto(proto,
                     IntentBindRecordProto.INTENT, false, true, false, false);
@@ -124,6 +120,8 @@
         if (binder != null) {
             proto.write(IntentBindRecordProto.BINDER, binder.toString());
         }
+        proto.write(IntentBindRecordProto.AUTO_CREATE,
+                (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
         proto.write(IntentBindRecordProto.REQUESTED, requested);
         proto.write(IntentBindRecordProto.RECEIVED, received);
         proto.write(IntentBindRecordProto.HAS_BOUND, hasBound);
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 72882de..0d7eab6 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -98,6 +98,14 @@
     }
 
     /**
+     * @return {@code true} if the keyguard is going away, {@code false} otherwise.
+     */
+    boolean isKeyguardGoingAway() {
+        // Also check keyguard showing in case value is stale.
+        return mKeyguardGoingAway && mKeyguardShowing;
+    }
+
+    /**
      * Update the Keyguard showing state.
      */
     void setKeyguardShown(boolean showing, int secondaryDisplayShowing) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index b6eff00..49a55cb 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -164,21 +164,20 @@
 
         public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
             long token = proto.start(fieldId);
-            proto.write(ServiceRecordProto.StartItemProto.ID, id);
+            proto.write(ServiceRecordProto.StartItem.ID, id);
             ProtoUtils.toDuration(proto,
-                    ServiceRecordProto.StartItemProto.DURATION, deliveredTime, now);
-            proto.write(ServiceRecordProto.StartItemProto.DELIVERY_COUNT, deliveryCount);
-            proto.write(ServiceRecordProto.StartItemProto.DONE_EXECUTING_COUNT, doneExecutingCount);
+                    ServiceRecordProto.StartItem.DURATION, deliveredTime, now);
+            proto.write(ServiceRecordProto.StartItem.DELIVERY_COUNT, deliveryCount);
+            proto.write(ServiceRecordProto.StartItem.DONE_EXECUTING_COUNT, doneExecutingCount);
             if (intent != null) {
-                intent.writeToProto(proto, ServiceRecordProto.StartItemProto.INTENT, true, true,
+                intent.writeToProto(proto, ServiceRecordProto.StartItem.INTENT, true, true,
                         true, false);
             }
             if (neededGrants != null) {
-                neededGrants.writeToProto(proto, ServiceRecordProto.StartItemProto.NEEDED_GRANTS);
+                neededGrants.writeToProto(proto, ServiceRecordProto.StartItem.NEEDED_GRANTS);
             }
             if (uriPermissions != null) {
-                uriPermissions.writeToProto(proto,
-                        ServiceRecordProto.StartItemProto.URI_PERMISSIONS);
+                uriPermissions.writeToProto(proto, ServiceRecordProto.StartItem.URI_PERMISSIONS);
             }
             proto.end(token);
         }
@@ -236,8 +235,6 @@
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
-        proto.write(ServiceRecordProto.HEX_HASH,
-                Integer.toHexString(System.identityHashCode(this)));
         proto.write(ServiceRecordProto.IS_RUNNING, app != null);
         if (app != null) {
             proto.write(ServiceRecordProto.PID, app.pid);
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 3886e5a..d49f3ba 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -148,7 +148,6 @@
 
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(UidRecordProto.HEX_HASH, Integer.toHexString(System.identityHashCode(this)));
         proto.write(UidRecordProto.UID, uid);
         proto.write(UidRecordProto.CURRENT, ProcessList.makeProcStateProtoEnum(curProcState));
         proto.write(UidRecordProto.EPHEMERAL, ephemeral);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 4fd8686..05549e7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -163,8 +163,14 @@
     private int mBatterySaverEnabledCount = 0;
 
     @GuardedBy("mLock")
+    private boolean mIsBatterySaverEnabled;
+
+    @GuardedBy("mLock")
     private long mLastBatterySaverEnabledTime = 0;
 
+    @GuardedBy("mLock")
+    private long mLastBatterySaverDisabledTime = 0;
+
     private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper();
 
     @VisibleForTesting
@@ -312,11 +318,12 @@
         final boolean newBatterySaverEnabled =
                 BatterySaverState.fromIndex(newState) != BatterySaverState.OFF;
         if (oldBatterySaverEnabled != newBatterySaverEnabled) {
+            mIsBatterySaverEnabled = newBatterySaverEnabled;
             if (newBatterySaverEnabled) {
                 mBatterySaverEnabledCount++;
                 mLastBatterySaverEnabledTime = injectCurrentTime();
             } else {
-                mLastBatterySaverEnabledTime = 0;
+                mLastBatterySaverDisabledTime = injectCurrentTime();
             }
         }
 
@@ -391,25 +398,35 @@
 
             indent = indent + "  ";
 
+            final long now = System.currentTimeMillis();
+            final long nowElapsed = injectCurrentTime();
+            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
             pw.print(indent);
-            pw.print("Battery Saver state: ");
-            if (mLastBatterySaverEnabledTime == 0) {
-                pw.print("OFF");
-            } else {
-                pw.print("ON since ");
-
-                final long now = System.currentTimeMillis();
-                final long nowElapsed = injectCurrentTime();
-
-                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+            pw.print("Battery Saver is currently: ");
+            pw.println(mIsBatterySaverEnabled ? "ON" : "OFF");
+            if (mLastBatterySaverEnabledTime > 0) {
+                pw.print(indent);
+                pw.print("  ");
+                pw.print("Last ON time: ");
                 pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverEnabledTime)));
-
                 pw.print(" ");
                 TimeUtils.formatDuration(mLastBatterySaverEnabledTime, nowElapsed, pw);
+                pw.println();
             }
-            pw.println();
+
+            if (mLastBatterySaverDisabledTime > 0) {
+                pw.print(indent);
+                pw.print("  ");
+                pw.print("Last OFF time: ");
+                pw.print(sdf.format(new Date(now - nowElapsed + mLastBatterySaverDisabledTime)));
+                pw.print(" ");
+                TimeUtils.formatDuration(mLastBatterySaverDisabledTime, nowElapsed, pw);
+                pw.println();
+            }
 
             pw.print(indent);
+            pw.print("  ");
             pw.print("Times enabled: ");
             pw.println(mBatterySaverEnabledCount);
 
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index f17bfa4..1a95fdd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -37,12 +37,15 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.servertransaction.DestroyActivityItem;
 import android.content.pm.ActivityInfo;
+import android.os.Debug;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -518,4 +521,37 @@
         assertTrue(mTask.mActivities.isEmpty());
         assertTrue(mStack.getAllTasks().isEmpty());
     }
+
+    @Test
+    public void testShouldSleepActivities() throws Exception {
+        // When focused activity and keyguard is going away, we should not sleep regardless
+        // of the display state
+        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, false /* expected*/);
+
+        // When not the focused stack, defer to display sleeping state.
+        verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, true /* expected*/);
+
+        // If keyguard is going away, defer to the display sleeping state.
+        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+                true /* displaySleeping */, true /* expected*/);
+        verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
+                false /* displaySleeping */, false /* expected*/);
+    }
+
+    private void verifyShouldSleepActivities(boolean focusedStack,
+            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
+        mSupervisor.mFocusedStack = focusedStack ? mStack : null;
+
+        final ActivityDisplay display = mock(ActivityDisplay.class);
+        final KeyguardController keyguardController = mSupervisor.getKeyguardController();
+
+        doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+        doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
+        doReturn(displaySleeping).when(display).isSleeping();
+
+        assertEquals(expected, mStack.shouldSleepActivities());
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 3041a5f..c130592 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -316,6 +316,7 @@
         @Override
         final protected ActivityStackSupervisor createStackSupervisor() {
             final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
+            final KeyguardController keyguardController = mock(KeyguardController.class);
 
             // No home stack is set.
             doNothing().when(supervisor).moveHomeStackToFront(any());
@@ -330,6 +331,7 @@
             doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
             // unit test version does not handle launch wake lock
             doNothing().when(supervisor).acquireLaunchWakelock();
+            doReturn(keyguardController).when(supervisor).getKeyguardController();
 
             supervisor.initialize();
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
new file mode 100644
index 0000000..6b52ee5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -0,0 +1,256 @@
+/*
+ * 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.server.usage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.PendingIntent;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class AppTimeLimitControllerTests {
+
+    private static final String PKG_SOC1 = "package.soc1";
+    private static final String PKG_SOC2 = "package.soc2";
+    private static final String PKG_GAME1 = "package.game1";
+    private static final String PKG_GAME2 = "package.game2";
+    private static final String PKG_PROD = "package.prod";
+
+    private static final int UID = 10100;
+    private static final int USER_ID = 10;
+    private static final int OBS_ID1 = 1;
+    private static final int OBS_ID2 = 2;
+    private static final int OBS_ID3 = 3;
+
+    private static final long TIME_30_MIN = 30 * 60_1000L;
+    private static final long TIME_10_MIN = 10 * 60_1000L;
+
+    private static final String[] GROUP1 = {
+            PKG_SOC1, PKG_GAME1, PKG_PROD
+    };
+
+    private static final String[] GROUP_SOC = {
+            PKG_SOC1, PKG_SOC2
+    };
+
+    private static final String[] GROUP_GAME = {
+            PKG_GAME1, PKG_GAME2
+    };
+
+    private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+
+    private AppTimeLimitController mController;
+
+    private HandlerThread mThread;
+
+    private long mUptimeMillis;
+
+    AppTimeLimitController.OnLimitReachedListener mListener
+            = new AppTimeLimitController.OnLimitReachedListener() {
+
+        @Override
+        public void onLimitReached(int observerId, int userId, long timeLimit, long timeElapsed,
+                PendingIntent callbackIntent) {
+            mCountDownLatch.countDown();
+        }
+    };
+
+    class MyAppTimeLimitController extends AppTimeLimitController {
+        MyAppTimeLimitController(AppTimeLimitController.OnLimitReachedListener listener,
+                Looper looper) {
+            super(listener, looper);
+        }
+
+        @Override
+        protected long getUptimeMillis() {
+            return mUptimeMillis;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mThread = new HandlerThread("Test");
+        mThread.start();
+        mController = new MyAppTimeLimitController(mListener, mThread.getLooper());
+    }
+
+    @After
+    public void tearDown() {
+        mThread.quit();
+    }
+
+    /** Verify observer is added */
+    @Test
+    public void testAddObserver() {
+        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasObserver(OBS_ID2));
+        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+    }
+
+    /** Verify observer is removed */
+    @Test
+    public void testRemoveObserver() {
+        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+        mController.removeObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+    }
+
+    /** Re-adding an observer should result in only one copy */
+    @Test
+    public void testObserverReAdd() {
+        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        assertTrue("Observer wasn't added", hasObserver(OBS_ID1));
+        addObserver(OBS_ID1, GROUP1, TIME_10_MIN);
+        assertTrue("Observer wasn't added",
+                mController.getObserverGroup(OBS_ID1, USER_ID).timeLimit == TIME_10_MIN);
+        mController.removeObserver(UID, OBS_ID1, USER_ID);
+        assertFalse("Observer wasn't removed", hasObserver(OBS_ID1));
+    }
+
+    /** Verify that usage across different apps within a group are added up */
+    @Test
+    public void testAccumulation() throws Exception {
+        setTime(0L);
+        addObserver(OBS_ID1, GROUP1, TIME_30_MIN);
+        moveToForeground(PKG_SOC1);
+        // Add 10 mins
+        setTime(TIME_10_MIN);
+        moveToBackground(PKG_SOC1);
+
+        long timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        assertEquals(TIME_10_MIN * 2, timeRemaining);
+
+        moveToForeground(PKG_SOC1);
+        setTime(TIME_10_MIN * 2);
+        moveToBackground(PKG_SOC1);
+
+        timeRemaining = mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining;
+        assertEquals(TIME_10_MIN, timeRemaining);
+
+        setTime(TIME_30_MIN);
+
+        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+
+        // Add a different package in the group
+        moveToForeground(PKG_GAME1);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        moveToBackground(PKG_GAME1);
+
+        assertEquals(0, mController.getObserverGroup(OBS_ID1, USER_ID).timeRemaining);
+        assertTrue(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify that time limit does not get triggered due to a different app */
+    @Test
+    public void testTimeoutOtherApp() throws Exception {
+        setTime(0L);
+        addObserver(OBS_ID1, GROUP1, 4_000L);
+        moveToForeground(PKG_SOC2);
+        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(6_000L);
+        moveToBackground(PKG_SOC2);
+        assertFalse(mCountDownLatch.await(100L, TimeUnit.MILLISECONDS));
+    }
+
+    /** Verify the timeout message is delivered at the right time */
+    @Test
+    public void testTimeout() throws Exception {
+        setTime(0L);
+        addObserver(OBS_ID1, GROUP1, 4_000L);
+        moveToForeground(PKG_SOC1);
+        setTime(6_000L);
+        assertTrue(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        moveToBackground(PKG_SOC1);
+        // Verify that the observer was removed
+        assertFalse(hasObserver(OBS_ID1));
+    }
+
+    /** If an app was already running, make sure it is partially counted towards the time limit */
+    @Test
+    public void testAlreadyRunning() throws Exception {
+        setTime(TIME_10_MIN);
+        moveToForeground(PKG_GAME1);
+        setTime(TIME_30_MIN);
+        addObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN);
+        setTime(TIME_30_MIN + TIME_10_MIN);
+        moveToBackground(PKG_GAME1);
+        assertFalse(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+
+        moveToForeground(PKG_GAME2);
+        setTime(TIME_30_MIN + TIME_30_MIN);
+        moveToBackground(PKG_GAME2);
+        assertTrue(mCountDownLatch.await(1000L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertFalse(hasObserver(OBS_ID2));
+    }
+
+    /** If watched app is already running, verify the timeout callback happens at the right time */
+    @Test
+    public void testAlreadyRunningTimeout() throws Exception {
+        setTime(0);
+        moveToForeground(PKG_SOC1);
+        setTime(TIME_10_MIN);
+        // 10 second time limit
+        addObserver(OBS_ID1, GROUP_SOC, 10_000L);
+        setTime(TIME_10_MIN + 5_000L);
+        // Shouldn't call back in 6 seconds
+        assertFalse(mCountDownLatch.await(6_000L, TimeUnit.MILLISECONDS));
+        setTime(TIME_10_MIN + 10_000L);
+        // Should call back by 11 seconds (6 earlier + 5 now)
+        assertTrue(mCountDownLatch.await(5_000L, TimeUnit.MILLISECONDS));
+        // Verify that the observer was removed
+        assertFalse(hasObserver(OBS_ID1));
+    }
+
+    private void moveToForeground(String packageName) {
+        mController.moveToForeground(packageName, "class", USER_ID);
+    }
+
+    private void moveToBackground(String packageName) {
+        mController.moveToBackground(packageName, "class", USER_ID);
+    }
+
+    private void addObserver(int observerId, String[] packages, long timeLimit) {
+        mController.addObserver(UID, observerId, packages, timeLimit, null, USER_ID);
+    }
+
+    /** Is there still an observer by that id */
+    private boolean hasObserver(int observerId) {
+        return mController.getObserverGroup(observerId, USER_ID) != null;
+    }
+
+    private void setTime(long time) {
+        mUptimeMillis = time;
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
new file mode 100644
index 0000000..9cd0593
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -0,0 +1,464 @@
+/**
+ * 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.server.usage;
+
+import android.annotation.UserIdInt;
+import android.app.PendingIntent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Monitors and informs of any app time limits exceeded. It must be informed when an app
+ * enters the foreground and exits. Used by UsageStatsService. Manages multiple users.
+ *
+ * Test: atest FrameworksServicesTests:AppTimeLimitControllerTests
+ * Test: manual: frameworks/base/tests/UsageStatsTest
+ */
+public class AppTimeLimitController {
+
+    private static final String TAG = "AppTimeLimitController";
+
+    private static final boolean DEBUG = false;
+
+    /** Lock class for this object */
+    private static class Lock {}
+
+    /** Lock object for the data in this class. */
+    private final Lock mLock = new Lock();
+
+    private final MyHandler mHandler;
+
+    private OnLimitReachedListener mListener;
+
+    @GuardedBy("mLock")
+    private final SparseArray<UserData> mUsers = new SparseArray<>();
+
+    private static class UserData {
+        /** userId of the user */
+        private @UserIdInt int userId;
+
+        /** The app that is currently in the foreground */
+        private String currentForegroundedPackage;
+
+        /** The time when the current app came to the foreground */
+        private long currentForegroundedTime;
+
+        /** The last app that was in the background */
+        private String lastBackgroundedPackage;
+
+        /** Map from package name for quick lookup */
+        private ArrayMap<String, ArrayList<TimeLimitGroup>> packageMap = new ArrayMap<>();
+
+        /** Map of observerId to details of the time limit group */
+        private SparseArray<TimeLimitGroup> groups = new SparseArray<>();
+
+        UserData(@UserIdInt int userId) {
+            this.userId = userId;
+        }
+    }
+
+    /**
+     * Listener interface for being informed when an app group's time limit is reached.
+     */
+    public interface OnLimitReachedListener {
+        /**
+         * Time limit for a group, keyed by the observerId, has been reached.
+         * @param observerId The observerId of the group whose limit was reached
+         * @param userId The userId
+         * @param timeLimit The original time limit in milliseconds
+         * @param timeElapsed How much time was actually spent on apps in the group, in milliseconds
+         * @param callbackIntent The PendingIntent to send when the limit is reached
+         */
+        public void onLimitReached(int observerId, @UserIdInt int userId, long timeLimit,
+                long timeElapsed, PendingIntent callbackIntent);
+    }
+
+    static class TimeLimitGroup {
+        int requestingUid;
+        int observerId;
+        String[] packages;
+        long timeLimit;
+        long timeRequested;
+        long timeRemaining;
+        PendingIntent callbackIntent;
+        String currentPackage;
+        long timeCurrentPackageStarted;
+        int userId;
+    }
+
+    class MyHandler extends Handler {
+
+        static final int MSG_CHECK_TIMEOUT = 1;
+        static final int MSG_INFORM_LISTENER = 2;
+
+        MyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CHECK_TIMEOUT:
+                    checkTimeout((TimeLimitGroup) msg.obj);
+                    break;
+                case MSG_INFORM_LISTENER:
+                    informListener((TimeLimitGroup) msg.obj);
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+            }
+        }
+    }
+
+    public AppTimeLimitController(OnLimitReachedListener listener, Looper looper) {
+        mHandler = new MyHandler(looper);
+        mListener = listener;
+    }
+
+    /** Overrideable by a test */
+    @VisibleForTesting
+    protected long getUptimeMillis() {
+        return SystemClock.uptimeMillis();
+    }
+
+    /** Returns an existing UserData object for the given userId, or creates one */
+    UserData getOrCreateUserDataLocked(int userId) {
+        UserData userData = mUsers.get(userId);
+        if (userData == null) {
+            userData = new UserData(userId);
+            mUsers.put(userId, userData);
+        }
+        return userData;
+    }
+
+    /** Clean up data if user is removed */
+    public void onUserRemoved(int userId) {
+        synchronized (mLock) {
+            // TODO: Remove any inflight delayed messages
+            mUsers.remove(userId);
+        }
+    }
+
+    /**
+     * Registers an observer with the given details. Existing observer with the same observerId
+     * is removed.
+     */
+    public void addObserver(int requestingUid, int observerId, String[] packages, long timeLimit,
+            PendingIntent callbackIntent, @UserIdInt int userId) {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+
+            removeObserverLocked(user, requestingUid, observerId);
+
+            TimeLimitGroup group = new TimeLimitGroup();
+            group.observerId = observerId;
+            group.callbackIntent = callbackIntent;
+            group.packages = packages;
+            group.timeLimit = timeLimit;
+            group.timeRemaining = group.timeLimit;
+            group.timeRequested = getUptimeMillis();
+            group.requestingUid = requestingUid;
+            group.timeCurrentPackageStarted = -1L;
+            group.userId = userId;
+
+            user.groups.append(observerId, group);
+
+            addGroupToPackageMapLocked(user, packages, group);
+
+            if (DEBUG) {
+                Slog.d(TAG, "addObserver " + packages + " for " + timeLimit);
+            }
+            // Handle the case where a target package is already in the foreground when observer
+            // is added.
+            if (user.currentForegroundedPackage != null && inPackageList(group.packages,
+                    user.currentForegroundedPackage)) {
+                group.timeCurrentPackageStarted = group.timeRequested;
+                group.currentPackage = user.currentForegroundedPackage;
+                if (group.timeRemaining > 0) {
+                    postCheckTimeoutLocked(group, group.timeRemaining);
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove a registered observer by observerId and calling uid.
+     * @param requestingUid The calling uid
+     * @param observerId The unique observer id for this user
+     * @param userId The user id of the observer
+     */
+    public void removeObserver(int requestingUid, int observerId, @UserIdInt int userId) {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            removeObserverLocked(user, requestingUid, observerId);
+        }
+    }
+
+    @VisibleForTesting
+    TimeLimitGroup getObserverGroup(int observerId, int userId) {
+        synchronized (mLock) {
+            return getOrCreateUserDataLocked(userId).groups.get(observerId);
+        }
+    }
+
+    private static boolean inPackageList(String[] packages, String packageName) {
+        return ArrayUtils.contains(packages, packageName);
+    }
+
+    @GuardedBy("mLock")
+    private void removeObserverLocked(UserData user, int requestingUid, int observerId) {
+        TimeLimitGroup group = user.groups.get(observerId);
+        if (group != null && group.requestingUid == requestingUid) {
+            removeGroupFromPackageMapLocked(user, group);
+            user.groups.remove(observerId);
+            mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
+        }
+    }
+
+    /**
+     * Called when an app has moved to the foreground.
+     * @param packageName The app that is foregrounded
+     * @param className The className of the activity
+     * @param userId The user
+     */
+    public void moveToForeground(String packageName, String className, int userId) {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            if (DEBUG) Slog.d(TAG, "Setting mCurrentForegroundedPackage to " + packageName);
+            // Note the current foreground package
+            user.currentForegroundedPackage = packageName;
+            user.currentForegroundedTime = getUptimeMillis();
+
+            // Check if the last package that was backgrounded is the same as this one
+            if (!TextUtils.equals(packageName, user.lastBackgroundedPackage)) {
+                // TODO: Move this logic up to usage stats to persist there.
+                incTotalLaunchesLocked(user, packageName);
+            }
+
+            // Check if any of the groups need to watch for this package
+            maybeWatchForPackageLocked(user, packageName, user.currentForegroundedTime);
+        }
+    }
+
+    /**
+     * Called when an app is sent to the background.
+     *
+     * @param packageName
+     * @param className
+     * @param userId
+     */
+    public void moveToBackground(String packageName, String className, int userId) {
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(userId);
+            user.lastBackgroundedPackage = packageName;
+            if (!TextUtils.equals(user.currentForegroundedPackage, packageName)) {
+                Slog.w(TAG, "Eh? Last foregrounded package = " + user.currentForegroundedPackage
+                        + " and now backgrounded = " + packageName);
+                return;
+            }
+            final long stopTime = getUptimeMillis();
+
+            // Add up the usage time to all groups that contain the package
+            ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
+            if (groups != null) {
+                final int size = groups.size();
+                for (int i = 0; i < size; i++) {
+                    final TimeLimitGroup group = groups.get(i);
+                    // Don't continue to send
+                    if (group.timeRemaining <= 0) continue;
+
+                    final long startTime = Math.max(user.currentForegroundedTime,
+                            group.timeRequested);
+                    long diff = stopTime - startTime;
+                    group.timeRemaining -= diff;
+                    if (group.timeRemaining <= 0) {
+                        if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + group.observerId);
+                        postInformListenerLocked(group);
+                    }
+                    // Reset indicators that observer was added when package was already fg
+                    group.currentPackage = null;
+                    group.timeCurrentPackageStarted = -1L;
+                    mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
+                }
+            }
+            user.currentForegroundedPackage = null;
+        }
+    }
+
+    private void postInformListenerLocked(TimeLimitGroup group) {
+        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MSG_INFORM_LISTENER,
+                group));
+    }
+
+    /**
+     * Inform the observer and unregister it, as the limit has been reached.
+     * @param group the observed group
+     */
+    private void informListener(TimeLimitGroup group) {
+        if (mListener != null) {
+            mListener.onLimitReached(group.observerId, group.userId, group.timeLimit,
+                    group.timeLimit - group.timeRemaining, group.callbackIntent);
+        }
+        // Unregister since the limit has been met and observer was informed.
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(group.userId);
+            removeObserverLocked(user, group.requestingUid, group.observerId);
+        }
+    }
+
+    /** Check if any of the groups care about this package and set up delayed messages */
+    @GuardedBy("mLock")
+    private void maybeWatchForPackageLocked(UserData user, String packageName, long uptimeMillis) {
+        ArrayList<TimeLimitGroup> groups = user.packageMap.get(packageName);
+        if (groups == null) return;
+
+        final int size = groups.size();
+        for (int i = 0; i < size; i++) {
+            TimeLimitGroup group = groups.get(i);
+            if (group.timeRemaining > 0) {
+                group.timeCurrentPackageStarted = uptimeMillis;
+                group.currentPackage = packageName;
+                if (DEBUG) {
+                    Slog.d(TAG, "Posting timeout for " + packageName + " for "
+                            + group.timeRemaining + "ms");
+                }
+                postCheckTimeoutLocked(group, group.timeRemaining);
+            }
+        }
+    }
+
+    private void addGroupToPackageMapLocked(UserData user, String[] packages,
+            TimeLimitGroup group) {
+        for (int i = 0; i < packages.length; i++) {
+            ArrayList<TimeLimitGroup> list = user.packageMap.get(packages[i]);
+            if (list == null) {
+                list = new ArrayList<>();
+                user.packageMap.put(packages[i], list);
+            }
+            list.add(group);
+        }
+    }
+
+    /**
+     * Remove the group reference from the package to group mapping, which is 1 to many.
+     * @param group The group to remove from the package map.
+     */
+    private void removeGroupFromPackageMapLocked(UserData user, TimeLimitGroup group) {
+        final int mapSize = user.packageMap.size();
+        for (int i = 0; i < mapSize; i++) {
+            ArrayList<TimeLimitGroup> list = user.packageMap.valueAt(i);
+            list.remove(group);
+        }
+    }
+
+    private void postCheckTimeoutLocked(TimeLimitGroup group, long timeout) {
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_CHECK_TIMEOUT, group),
+                timeout);
+    }
+
+    /**
+     * See if the given group has reached the timeout if the current foreground app is included
+     * and it exceeds the time remaining.
+     * @param group the group of packages to check
+     */
+    void checkTimeout(TimeLimitGroup group) {
+        // For each package in the group, check if any of the currently foregrounded apps are adding
+        // up to hit the limit and inform the observer
+        synchronized (mLock) {
+            UserData user = getOrCreateUserDataLocked(group.userId);
+            // This group doesn't exist anymore, nothing to see here.
+            if (user.groups.get(group.observerId) != group) return;
+
+            if (DEBUG) Slog.d(TAG, "checkTimeout timeRemaining=" + group.timeRemaining);
+
+            // Already reached the limit, no need to report again
+            if (group.timeRemaining <= 0) return;
+
+            if (DEBUG) {
+                Slog.d(TAG, "checkTimeout foregroundedPackage="
+                        + user.currentForegroundedPackage);
+            }
+
+            if (inPackageList(group.packages, user.currentForegroundedPackage)) {
+                if (DEBUG) {
+                    Slog.d(TAG, "checkTimeout package in foreground="
+                            + user.currentForegroundedPackage);
+                }
+                if (group.timeCurrentPackageStarted < 0) {
+                    Slog.w(TAG, "startTime was not set correctly for " + group);
+                }
+                final long timeInForeground = getUptimeMillis() - group.timeCurrentPackageStarted;
+                if (group.timeRemaining <= timeInForeground) {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Time limit reached");
+                    // Hit the limit, set timeRemaining to zero to avoid checking again
+                    group.timeRemaining -= timeInForeground;
+                    postInformListenerLocked(group);
+                    // Reset
+                    group.timeCurrentPackageStarted = -1L;
+                    group.currentPackage = null;
+                } else {
+                    if (DEBUG) Slog.d(TAG, "checkTimeout : Some more time remaining");
+                    postCheckTimeoutLocked(group, group.timeRemaining - timeInForeground);
+                }
+            }
+        }
+    }
+
+    private void incTotalLaunchesLocked(UserData user, String packageName) {
+        // TODO: Inform UsageStatsService and aggregate the counter per app
+    }
+
+    void dump(PrintWriter pw) {
+        synchronized (mLock) {
+            pw.println("\n  App Time Limits");
+            int nUsers = mUsers.size();
+            for (int i = 0; i < nUsers; i++) {
+                UserData user = mUsers.valueAt(i);
+                pw.print("   User "); pw.println(user.userId);
+                int nGroups = user.groups.size();
+                for (int j = 0; j < nGroups; j++) {
+                    TimeLimitGroup group = user.groups.valueAt(j);
+                    pw.print("    Group id="); pw.print(group.observerId);
+                    pw.print(" timeLimit="); pw.print(group.timeLimit);
+                    pw.print(" remaining="); pw.print(group.timeRemaining);
+                    pw.print(" currentPackage="); pw.print(group.currentPackage);
+                    pw.print(" timeCurrentPkgStarted="); pw.print(group.timeCurrentPackageStarted);
+                    pw.print(" packages="); pw.println(Arrays.toString(group.packages));
+                }
+                pw.println();
+                pw.print("    currentForegroundedPackage=");
+                pw.println(user.currentForegroundedPackage);
+                pw.print("    lastBackgroundedPackage="); pw.println(user.lastBackgroundedPackage);
+            }
+        }
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 9be9f3f..b144545c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
+import android.app.PendingIntent;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
@@ -72,6 +73,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A service that collects, aggregates, and persists application usage data.
@@ -117,6 +119,8 @@
 
     AppStandbyController mAppStandby;
 
+    AppTimeLimitController mAppTimeLimit;
+
     private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
             new UsageStatsManagerInternal.AppIdleStateChangeListener() {
                 @Override
@@ -151,6 +155,20 @@
 
         mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
 
+        mAppTimeLimit = new AppTimeLimitController(
+                (observerId, userId, timeLimit, timeElapsed, callbackIntent) -> {
+                    Intent intent = new Intent();
+                    intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
+                    intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
+                    intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
+                    try {
+                        callbackIntent.send(getContext(), 0, intent);
+                    } catch (PendingIntent.CanceledException e) {
+                        Slog.w(TAG, "Couldn't deliver callback: "
+                                + callbackIntent);
+                    }
+                }, mHandler.getLooper());
+
         mAppStandby.addListener(mStandbyChangeListener);
         File systemDataDir = new File(Environment.getDataDirectory(), "system");
         mUsageStatsDir = new File(systemDataDir, "usagestats");
@@ -374,6 +392,16 @@
             service.reportEvent(event);
 
             mAppStandby.reportEvent(event, elapsedRealtime, userId);
+            switch (event.mEventType) {
+                case Event.MOVE_TO_FOREGROUND:
+                    mAppTimeLimit.moveToForeground(event.getPackageName(), event.getClassName(),
+                            userId);
+                    break;
+                case Event.MOVE_TO_BACKGROUND:
+                    mAppTimeLimit.moveToBackground(event.getPackageName(), event.getClassName(),
+                            userId);
+                    break;
+            }
         }
     }
 
@@ -394,6 +422,7 @@
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
             mAppStandby.onUserRemoved(userId);
+            mAppTimeLimit.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -549,6 +578,8 @@
                 pw.println();
                 mAppStandby.dumpState(args, pw);
             }
+
+            mAppTimeLimit.dump(pw);
         }
     }
 
@@ -927,6 +958,60 @@
 
             mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
         }
+
+        @Override
+        public void registerAppUsageObserver(int observerId,
+                String[] packages, long timeLimitMs, PendingIntent
+                callbackIntent, String callingPackage) {
+            if (!hasPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have PACKAGE_USAGE_STATS permission");
+            }
+
+            if (packages == null || packages.length == 0) {
+                throw new IllegalArgumentException("Must specify at least one package");
+            }
+            if (timeLimitMs <= 0) {
+                throw new IllegalArgumentException("Time limit must be > 0");
+            }
+            if (callbackIntent == null) {
+                throw new NullPointerException("callbackIntent can't be null");
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.registerAppUsageObserver(callingUid, observerId,
+                        packages, timeLimitMs, callbackIntent, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void unregisterAppUsageObserver(int observerId, String callingPackage) {
+            if (!hasPermission(callingPackage)) {
+                throw new SecurityException("Caller doesn't have PACKAGE_USAGE_STATS permission");
+            }
+
+            final int callingUid = Binder.getCallingUid();
+            final int userId = UserHandle.getUserId(callingUid);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                UsageStatsService.this.unregisterAppUsageObserver(callingUid, observerId, userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
+            long timeLimitMs, PendingIntent callbackIntent, int userId) {
+        mAppTimeLimit.addObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+                userId);
+    }
+
+    void unregisterAppUsageObserver(int callingUid, int observerId, int userId) {
+        mAppTimeLimit.removeObserver(callingUid, observerId, userId);
     }
 
     /**
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 4f78c4c..f1653ce 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3165,8 +3165,8 @@
             values.put(RIL_VOICE_RADIO_TECHNOLOGY, state.getRilVoiceRadioTechnology());
             values.put(RIL_DATA_RADIO_TECHNOLOGY, state.getRilDataRadioTechnology());
             values.put(CSS_INDICATOR, state.getCssIndicator());
-            values.put(NETWORK_ID, state.getNetworkId());
-            values.put(SYSTEM_ID, state.getSystemId());
+            values.put(NETWORK_ID, state.getCdmaNetworkId());
+            values.put(SYSTEM_ID, state.getCdmaSystemId());
             values.put(CDMA_ROAMING_INDICATOR, state.getCdmaRoamingIndicator());
             values.put(CDMA_DEFAULT_ROAMING_INDICATOR, state.getCdmaDefaultRoamingIndicator());
             values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex());
@@ -3296,13 +3296,13 @@
         public static final String CSS_INDICATOR = "css_indicator";
 
         /**
-         * This is the same as {@link ServiceState#getNetworkId()}.
+         * This is the same as {@link ServiceState#getCdmaNetworkId()}.
          * @hide
          */
         public static final String NETWORK_ID = "network_id";
 
         /**
-         * This is the same as {@link ServiceState#getSystemId()}.
+         * This is the same as {@link ServiceState#getCdmaSystemId()}.
          * @hide
          */
         public static final String SYSTEM_ID = "system_id";
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index a9c1cf6..9c831b9 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -221,7 +221,7 @@
     public static final int ROAMING_TYPE_INTERNATIONAL = 3;
 
     /**
-     * Unknown ID. Could be returned by {@link #getNetworkId()} or {@link #getSystemId()}
+     * Unknown ID. Could be returned by {@link #getCdmaNetworkId()} or {@link #getCdmaSystemId()}
      */
     public static final int UNKNOWN_ID = -1;
 
@@ -1222,7 +1222,7 @@
 
     /** @hide */
     @TestApi
-    public void setSystemAndNetworkId(int systemId, int networkId) {
+    public void setCdmaSystemAndNetworkId(int systemId, int networkId) {
         this.mSystemId = systemId;
         this.mNetworkId = networkId;
     }
@@ -1388,7 +1388,7 @@
      * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getNetworkId() {
+    public int getCdmaNetworkId() {
         return this.mNetworkId;
     }
 
@@ -1397,7 +1397,7 @@
      * system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getSystemId() {
+    public int getCdmaSystemId() {
         return this.mSystemId;
     }
 
diff --git a/tests/UsageStatsTest/AndroidManifest.xml b/tests/UsageStatsTest/AndroidManifest.xml
index c27be7b..66af454 100644
--- a/tests/UsageStatsTest/AndroidManifest.xml
+++ b/tests/UsageStatsTest/AndroidManifest.xml
@@ -21,5 +21,6 @@
         </activity>
 
         <activity android:name=".UsageLogActivity" />
+
     </application>
 </manifest>
diff --git a/tests/UsageStatsTest/res/menu/main.xml b/tests/UsageStatsTest/res/menu/main.xml
index 4ccbc81..612267c 100644
--- a/tests/UsageStatsTest/res/menu/main.xml
+++ b/tests/UsageStatsTest/res/menu/main.xml
@@ -4,4 +4,6 @@
         android:title="View Log"/>
     <item android:id="@+id/call_is_app_inactive"
         android:title="Call isAppInactive()"/>
+    <item android:id="@+id/set_app_limit"
+        android:title="Set App Limit" />
 </menu>
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 9429d9b..3d8ce21 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.AlertDialog;
 import android.app.ListActivity;
+import android.app.PendingIntent;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
@@ -36,14 +37,17 @@
 import android.widget.BaseAdapter;
 import android.widget.EditText;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 public class UsageStatsActivity extends ListActivity {
     private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
+    private static final String EXTRA_KEY_TIMEOUT = "com.android.tests.usagestats.extra.TIMEOUT";
     private UsageStatsManager mUsageStatsManager;
     private Adapter mAdapter;
     private Comparator<UsageStats> mComparator = new Comparator<UsageStats>() {
@@ -59,6 +63,20 @@
         mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
         mAdapter = new Adapter();
         setListAdapter(mAdapter);
+        Bundle extras = getIntent().getExtras();
+        if (extras != null && extras.containsKey(UsageStatsManager.EXTRA_TIME_USED)) {
+            System.err.println("UsageStatsActivity " + extras);
+            Toast.makeText(this, "Timeout of observed app\n" + extras, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        Bundle extras = intent.getExtras();
+        if (extras != null && extras.containsKey(UsageStatsManager.EXTRA_TIME_USED)) {
+            System.err.println("UsageStatsActivity " + extras);
+            Toast.makeText(this, "Timeout of observed app\n" + extras, Toast.LENGTH_SHORT).show();
+        }
     }
 
     @Override
@@ -77,7 +95,9 @@
             case R.id.call_is_app_inactive:
                 callIsAppInactive();
                 return true;
-
+            case R.id.set_app_limit:
+                callSetAppLimit();
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -116,6 +136,40 @@
         builder.show();
     }
 
+    private void callSetAppLimit() {
+        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle("Enter package name");
+        final EditText input = new EditText(this);
+        input.setInputType(InputType.TYPE_CLASS_TEXT);
+        input.setHint("com.android.tests.usagestats");
+        builder.setView(input);
+
+        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                final String packageName = input.getText().toString().trim();
+                if (!TextUtils.isEmpty(packageName)) {
+                    String[] packages = packageName.split(",");
+                    Intent intent = new Intent(Intent.ACTION_MAIN);
+                    intent.setClass(UsageStatsActivity.this, UsageStatsActivity.class);
+                    intent.setPackage(getPackageName());
+                    intent.putExtra(EXTRA_KEY_TIMEOUT, true);
+                    mUsageStatsManager.registerAppUsageObserver(1, packages,
+                            30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+                                    1, intent, 0));
+                }
+            }
+        });
+        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.cancel();
+            }
+        });
+
+        builder.show();
+    }
+
     private void showInactive(String packageName) {
         final AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setMessage(
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6865f77..21ae3a9 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -529,91 +529,6 @@
     /** @hide **/
     public static int INVALID_RSSI = -127;
 
-    /**
-     * @hide
-     * A summary of the RSSI and Band status for that configuration
-     * This is used as a temporary value by the auto-join controller
-     */
-    public static final class Visibility {
-        public int rssi5;   // strongest 5GHz RSSI
-        public int rssi24;  // strongest 2.4GHz RSSI
-        public int num5;    // number of BSSIDs on 5GHz
-        public int num24;   // number of BSSIDs on 2.4GHz
-        public long age5;   // timestamp of the strongest 5GHz BSSID (last time it was seen)
-        public long age24;  // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
-        public String BSSID24;
-        public String BSSID5;
-        public int score; // Debug only, indicate last score used for autojoin/cell-handover
-        public int currentNetworkBoost; // Debug only, indicate boost applied to RSSI if current
-        public int bandPreferenceBoost; // Debug only, indicate boost applied to RSSI if current
-        public int lastChoiceBoost; // Debug only, indicate last choice applied to this configuration
-        public String lastChoiceConfig; // Debug only, indicate last choice applied to this configuration
-
-        public Visibility() {
-            rssi5 = INVALID_RSSI;
-            rssi24 = INVALID_RSSI;
-        }
-
-        public Visibility(Visibility source) {
-            rssi5 = source.rssi5;
-            rssi24 = source.rssi24;
-            age24 = source.age24;
-            age5 = source.age5;
-            num24 = source.num24;
-            num5 = source.num5;
-            BSSID5 = source.BSSID5;
-            BSSID24 = source.BSSID24;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sbuf = new StringBuilder();
-            sbuf.append("[");
-            if (rssi24 > INVALID_RSSI) {
-                sbuf.append(Integer.toString(rssi24));
-                sbuf.append(",");
-                sbuf.append(Integer.toString(num24));
-                if (BSSID24 != null) sbuf.append(",").append(BSSID24);
-            }
-            sbuf.append("; ");
-            if (rssi5 > INVALID_RSSI) {
-                sbuf.append(Integer.toString(rssi5));
-                sbuf.append(",");
-                sbuf.append(Integer.toString(num5));
-                if (BSSID5 != null) sbuf.append(",").append(BSSID5);
-            }
-            if (score != 0) {
-                sbuf.append("; ").append(score);
-                sbuf.append(", ").append(currentNetworkBoost);
-                sbuf.append(", ").append(bandPreferenceBoost);
-                if (lastChoiceConfig != null) {
-                    sbuf.append(", ").append(lastChoiceBoost);
-                    sbuf.append(", ").append(lastChoiceConfig);
-                }
-            }
-            sbuf.append("]");
-            return sbuf.toString();
-        }
-    }
-
-    /** @hide
-     * Cache the visibility status of this configuration.
-     * Visibility can change at any time depending on scan results availability.
-     * Owner of the WifiConfiguration is responsible to set this field based on
-     * recent scan results.
-     ***/
-    public Visibility visibility;
-
-    /** @hide
-     * calculate and set Visibility for that configuration.
-     *
-     * age in milliseconds: we will consider only ScanResults that are more recent,
-     * i.e. younger.
-     ***/
-    public void setVisibility(Visibility status) {
-        visibility = status;
-    }
-
     // States for the userApproved field
     /**
      * @hide
@@ -2178,9 +2093,6 @@
             meteredHint = source.meteredHint;
             meteredOverride = source.meteredOverride;
             useExternalScores = source.useExternalScores;
-            if (source.visibility != null) {
-                visibility = new Visibility(source.visibility);
-            }
 
             didSelfAdd = source.didSelfAdd;
             lastConnectUid = source.lastConnectUid;