Merge "Change alarm/ringer status QS separator." into pi-dev
diff --git a/Android.mk b/Android.mk
index e219661..d7d9c90 100644
--- a/Android.mk
+++ b/Android.mk
@@ -252,6 +252,11 @@
 		-federate SupportLib https://developer.android.com \
 		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
 
+# Federate AndroidX references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+		-federate AndroidX https://developer.android.com \
+		-federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
+
 # ====  Public API diff ===========================
 include $(CLEAR_VARS)
 
diff --git a/api/current.txt b/api/current.txt
index 406ebac..670f6ca 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6115,8 +6115,12 @@
     method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+    method public void grantRuntimePermission(java.lang.String, java.lang.String);
+    method public void grantRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
     method public boolean performGlobalAction(int);
+    method public void revokeRuntimePermission(java.lang.String, java.lang.String);
+    method public void revokeRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
     method public boolean setRotation(int);
     method public void setRunAsMonkey(boolean);
@@ -6501,7 +6505,7 @@
     method public boolean installExistingPackage(android.content.ComponentName, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
-    method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean);
+    method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6685,6 +6689,8 @@
     field public static final int ID_TYPE_IMEI = 4; // 0x4
     field public static final int ID_TYPE_MEID = 8; // 0x8
     field public static final int ID_TYPE_SERIAL = 2; // 0x2
+    field public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1; // 0x1
+    field public static final int INSTALLKEY_SET_USER_SELECTABLE = 2; // 0x2
     field public static final int KEYGUARD_DISABLE_BIOMETRICS = 416; // 0x1a0
     field public static final int KEYGUARD_DISABLE_FACE = 128; // 0x80
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
@@ -13644,7 +13650,6 @@
     method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
     method public android.graphics.PostProcessor getPostProcessor();
     method public boolean getRequireUnpremultiplied();
-    method public android.util.Size getSampledSize(int);
     method public android.graphics.ImageDecoder setAllocator(int);
     method public android.graphics.ImageDecoder setConserveMemory(boolean);
     method public android.graphics.ImageDecoder setCrop(android.graphics.Rect);
@@ -13653,8 +13658,8 @@
     method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
     method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor);
     method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
-    method public android.graphics.ImageDecoder setResize(int, int);
-    method public android.graphics.ImageDecoder setResize(int);
+    method public android.graphics.ImageDecoder setSampleSize(int);
+    method public android.graphics.ImageDecoder setTargetSize(int, int);
     field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
     field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
     field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
diff --git a/api/removed.txt b/api/removed.txt
index 9fe25c9..5863e77 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -184,6 +184,8 @@
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public deprecated boolean getAsAlphaMask();
     method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+    method public deprecated android.graphics.ImageDecoder setResize(int, int);
+    method public deprecated android.graphics.ImageDecoder setResize(int);
     field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
     field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
     field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
diff --git a/api/test-current.txt b/api/test-current.txt
index d323725..b520dfb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,8 +1,10 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
   }
 
 }
@@ -127,8 +129,8 @@
   public final class UiAutomation {
     method public void destroy();
     method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
-    method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
   }
 
   public class UiModeManager {
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 3af6105..70e3fad 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,4 +1,5 @@
 Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+Landroid/accounts/IAccountAuthenticatorResponse$Stub;-><init>()V
 Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
 Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/animation/LayoutTransition;->cancel()V
@@ -236,6 +237,7 @@
 Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
 Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
 Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
+Landroid/app/IActivityManager;->requestBugReport(I)V
 Landroid/app/IActivityManager;->resumeAppSwitches()V
 Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
 Landroid/app/IActivityManager;->setTaskResizeable(II)V
@@ -609,6 +611,11 @@
 Landroid/graphics/Camera;->native_instance:J
 Landroid/graphics/Canvas;-><init>(J)V
 Landroid/graphics/Canvas;->release()V
+Landroid/graphics/Canvas;->save(I)I
+Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
+Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
 Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
 Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
 Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
@@ -943,6 +950,7 @@
 Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
 Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V
 Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
 Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
@@ -995,6 +1003,8 @@
 Landroid/media/RemoteDisplay;->notifyDisplayConnected(Landroid/view/Surface;IIII)V
 Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
 Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
+Landroid/media/RemoteDisplayState;->displays:Ljava/util/ArrayList;
+Landroid/media/RemoteDisplayState;-><init>()V
 Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
 Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
 Landroid/media/session/MediaSession;->mCallback:Landroid/media/session/MediaSession$CallbackMessageHandler;
@@ -1006,6 +1016,11 @@
 Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
 Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/SubtitleTrack$RenderingWidget;->draw(Landroid/graphics/Canvas;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->onAttachedToWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->onDetachedFromWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->setOnChangedListener(Landroid/media/SubtitleTrack$RenderingWidget$OnChangedListener;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->setSize(II)V
 Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
 Landroid/media/ToneGenerator;->mNativeContext:J
 Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -1111,6 +1126,8 @@
 Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I
 Landroid/net/wifi/ScanResult;->anqpDomainId:I
 Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List;
 Landroid/net/wifi/ScanResult;->distanceCm:I
@@ -1961,6 +1978,7 @@
 Landroid/util/Rational;->mNumerator:I
 Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
 Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->w(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/util/SparseIntArray;->mKeys:[I
 Landroid/util/SparseIntArray;->mSize:I
 Landroid/util/SparseIntArray;->mValues:[I
@@ -2280,6 +2298,7 @@
 Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/webkit/WebResourceResponse;->mImmutable:Z
 Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
+Landroid/webkit/WebViewClient;->onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V
 Landroid/webkit/WebView;->debugDump()V
 Landroid/webkit/WebView;->disablePlatformNotifications()V
 Landroid/webkit/WebView;->emulateShiftHeld()V
@@ -2709,11 +2728,15 @@
 Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I
 Lcom/android/internal/R$styleable;->CompoundButton_button:I
 Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I
+Lcom/android/internal/R$styleable;->DialogPreference:[I
 Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I
 Lcom/android/internal/R$styleable;->EdgeEffect:[I
 Lcom/android/internal/R$styleable;->IconMenuView:[I
 Lcom/android/internal/R$styleable;->ImageView:[I
 Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ListPreference_entries:I
+Lcom/android/internal/R$styleable;->ListPreference:[I
 Lcom/android/internal/R$styleable;->Preference_defaultValue:I
 Lcom/android/internal/R$styleable;->Preference_dependency:I
 Lcom/android/internal/R$styleable;->Preference_enabled:I
@@ -2779,6 +2802,7 @@
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/util/AsyncChannel;->sendMessage(III)V
 Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
 Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
 Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
@@ -2955,6 +2979,7 @@
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
 Ljava/lang/Short;->value:S
 Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([II[III)V
 Ljava/lang/System;-><init>()V
 Ljava/lang/Thread;->daemon:Z
 Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
@@ -3066,5 +3091,7 @@
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V
 Lorg/json/JSONArray;->values:Ljava/util/List;
+Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
+Lorg/json/JSONObject;->keySet()Ljava/util/Set;
 Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 77bb0bb..61ae6e7 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -32,7 +32,6 @@
 Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
 Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->requestBugReport(I)V
 Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
 Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
 Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
@@ -108,11 +107,6 @@
 Landroid/graphics/Bitmap;->createHardwareBitmap(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;
 Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;Landroid/graphics/Region$Op;)Z
 Landroid/graphics/Canvas;->clipRegion(Landroid/graphics/Region;)Z
-Landroid/graphics/Canvas;->save(I)I
-Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
-Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
-Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
-Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
 Landroid/graphics/drawable/Drawable;->isProjected()Z
 Landroid/graphics/drawable/Drawable;->updateTintFilter(Landroid/graphics/PorterDuffColorFilter;Landroid/content/res/ColorStateList;Landroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuffColorFilter;
 Landroid/hardware/camera2/CaptureRequest$Key;-><init>(Ljava/lang/String;Ljava/lang/Class;)V
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..bd4933a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -47,6 +46,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -876,15 +876,35 @@
     }
 
     /**
+     * Grants a runtime permission to a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to grant the permission.
+     */
+    public void grantRuntimePermission(String packageName, String permission) {
+        grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public boolean grantRuntimePermission(String packageName, String permission,
+            UserHandle userHandle) {
+        grantRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
      * Grants a runtime permission to a package for a user.
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
-     * @return Whether granting succeeded.
-     *
-     * @hide
+     * @throws SecurityException if unable to grant the permission.
      */
-    @TestApi
-    public boolean grantRuntimePermission(String packageName, String permission,
+    public void grantRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
@@ -896,25 +916,42 @@
             // Calling out without a lock held.
             mUiAutomationConnection.grantRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error granting runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
-     * Revokes a runtime permission from a package for a user.
-     * @param packageName The package from which to revoke.
-     * @param permission The permission to revoke.
-     * @return Whether revoking succeeded.
-     *
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermission(String packageName, String permission) {
+        revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
      * @hide
      */
+    @Deprecated
     @TestApi
     public boolean revokeRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
+        revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermissionAsUser(String packageName, String permission,
+            UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
@@ -925,12 +962,9 @@
             // Calling out without a lock held.
             mUiAutomationConnection.revokeRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error revoking runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
@@ -949,6 +983,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source = null;
         ParcelFileDescriptor sink = null;
@@ -991,6 +1026,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source_read = null;
         ParcelFileDescriptor sink_read = null;
@@ -1056,6 +1092,16 @@
         }
     }
 
+    private void warnIfBetterCommand(String cmd) {
+        if (cmd.startsWith("pm grant ")) {
+            Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm grant'");
+        } else if (cmd.startsWith("pm revoke ")) {
+            Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm revoke'");
+        }
+    }
+
     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
 
         public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 106b42f..1534a15 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -124,6 +124,7 @@
 @SystemService(Context.DEVICE_POLICY_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
 public class DevicePolicyManager {
+
     private static String TAG = "DevicePolicyManager";
 
     private final Context mContext;
@@ -1751,6 +1752,25 @@
     public static final int ID_TYPE_MEID = 8;
 
     /**
+     * Specifies that the calling app should be granted access to the installed credentials
+     * immediately. Otherwise, access to the credentials will be gated by user approval.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1;
+
+    /**
+     * Specifies that a user can select the key via the Certificate Selection prompt.
+     * If this flag is not set when calling {@link #installKeyPair}, the key can only be granted
+     * access by implementing {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_SET_USER_SELECTABLE = 2;
+
+    /**
      * Broadcast action: sent when the profile owner is set, changed or cleared.
      *
      * This broadcast is sent only to the user managed by the new profile owner.
@@ -4126,7 +4146,11 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
-        return installKeyPair(admin, privKey, certs, alias, requestAccess, true);
+        int flags = INSTALLKEY_SET_USER_SELECTABLE;
+        if (requestAccess) {
+            flags |= INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        }
+        return installKeyPair(admin, privKey, certs, alias, flags);
     }
 
     /**
@@ -4150,13 +4174,9 @@
      *        {@link android.security.KeyChain#getCertificateChain}.
      * @param alias The private key alias under which to install the certificate. If a certificate
      *        with that alias already exists, it will be overwritten.
-     * @param requestAccess {@code true} to request that the calling app be granted access to the
-     *        credentials immediately. Otherwise, access to the credentials will be gated by user
-     *        approval.
-     * @param isUserSelectable {@code true} to indicate that a user can select this key via the
-     *        Certificate Selection prompt, false to indicate that this key can only be granted
-     *        access by implementing
-     *        {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * @param flags Flags to request that the calling app be granted access to the credentials
+     *        and set the key to be user-selectable. See {@link #INSTALLKEY_SET_USER_SELECTABLE} and
+     *        {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
      * @return {@code true} if the keys were installed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
@@ -4165,9 +4185,12 @@
      * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
-            @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess,
-            boolean isUserSelectable) {
+            @NonNull Certificate[] certs, @NonNull String alias, int flags) {
         throwIfParentInstance("installKeyPair");
+        boolean requestAccess = (flags & INSTALLKEY_REQUEST_CREDENTIALS_ACCESS)
+                == INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        boolean isUserSelectable = (flags & INSTALLKEY_SET_USER_SELECTABLE)
+                == INSTALLKEY_SET_USER_SELECTABLE;
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -4246,6 +4269,8 @@
      *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
      *         or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
      *         {@code keySpec} does not contain an attestation challenge.
+     * @throws UnsupportedOperationException if Device ID attestation was requested but the
+     *         underlying hardware does not support it.
      * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
      */
     public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 70f343e..b7f6cde 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7809,6 +7809,14 @@
                 "suppress_auto_battery_saver_suggestion";
 
         /**
+         * List of packages, which data need to be unconditionally cleared before full restore.
+         * Type: string
+         * @hide
+         */
+        public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
+                "packages_to_clear_data_before_full_restore";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index cfb8980..593747d 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -145,6 +145,7 @@
         optional SettingProto transport = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto manager_constants = 5;
         optional SettingProto local_transport_parameters = 6;
+        optional SettingProto packages_to_clear_data_before_full_restore = 7;
     }
     optional Backup backup = 10;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 48d394a..cb375d5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2016,11 +2016,11 @@
     <permission android:name="android.permission.REMOVE_TASKS"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+    <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
     <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
         android:protectionLevel="signature|privileged|development" />
 
-    <!-- @SystemApi @hide Allows an application to embed other activities -->
+    <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
     <permission android:name="android.permission.ACTIVITY_EMBEDDING"
                 android:protectionLevel="signature|privileged|development" />
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 2a3fcad..4b9465d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -587,7 +587,8 @@
                  Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
                  Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
                  Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
-                 Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION);
+                 Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
+                 Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 5ca0ad6..6939907 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -766,7 +766,7 @@
      *
      *  <p>This takes an input that functions like
      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
-     *  height that can be acheived by sampling the encoded image. Other widths
+     *  height that can be achieved by sampling the encoded image. Other widths
      *  and heights may be supported, but will require an additional (internal)
      *  scaling step. Such internal scaling is *not* supported with
      *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
@@ -774,6 +774,8 @@
      *  @param sampleSize Sampling rate of the encoded image.
      *  @return {@link android.util.Size} of the width and height after
      *      sampling.
+     *
+     *  @hide
      */
     @NonNull
     public Size getSampledSize(int sampleSize) {
@@ -789,14 +791,28 @@
     }
 
     // Modifiers
+    /** @removed
+     * @deprecated Renamed to {@link #setTargetSize}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setResize(int width, int height) {
+        return this.setTargetSize(width, height);
+    }
+
     /**
-     *  Resize the output to have the following size.
+     *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
+     *
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
+     *  <p>Only the last call to this or {@link #setSampleSize} is respected.</p>
      *
      *  @param width must be greater than 0.
      *  @param height must be greater than 0.
      *  @return this object for chaining.
      */
-    public ImageDecoder setResize(int width, int height) {
+    public ImageDecoder setTargetSize(int width, int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -807,18 +823,65 @@
         return this;
     }
 
+    /** @removed
+     * @deprecated Renamed to {@link #setSampleSize}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setResize(int sampleSize) {
+        return this.setSampleSize(sampleSize);
+    }
+
+    private int getTargetDimension(int original, int sampleSize, int computed) {
+        // Sampling will never result in a smaller size than 1.
+        if (sampleSize >= original) {
+            return 1;
+        }
+
+        // Use integer divide to find the desired size. If that is what
+        // getSampledSize computed, that is the size to use.
+        int target = original / sampleSize;
+        if (computed == target) {
+            return computed;
+        }
+
+        // If sampleSize does not divide evenly into original, the decoder
+        // may round in either direction. It just needs to get a result that
+        // is close.
+        int reverse = computed * sampleSize;
+        if (Math.abs(reverse - original) < sampleSize) {
+            // This is the size that can be decoded most efficiently.
+            return computed;
+        }
+
+        // The decoder could not get close (e.g. it is a DNG image).
+        return target;
+    }
+
     /**
-     *  Resize based on a sample size.
+     *  Set the target size with a sampleSize.
      *
-     *  <p>This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
+     *  <p>Requests the decoder to subsample the original image, returning a
+     *  smaller image to save memory. The sample size is the number of pixels
+     *  in either dimension that correspond to a single pixel in the output.
+     *  For example, sampleSize == 4 returns an image that is 1/4 the
+     *  width/height of the original, and 1/16 the number of pixels.</p>
+     *
+     *  <p>Must be greater than or equal to 1.</p>
+     *
+     *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
      *  @return this object for chaining.
      */
-    public ImageDecoder setResize(int sampleSize) {
+    public ImageDecoder setSampleSize(int sampleSize) {
         Size size = this.getSampledSize(sampleSize);
-        return this.setResize(size.getWidth(), size.getHeight());
+        int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
+        int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
+        return this.setTargetSize(targetWidth, targetHeight);
     }
 
     private boolean requestedResize() {
@@ -972,8 +1035,8 @@
      *  Crop the output to {@code subset} of the (possibly) scaled image.
      *
      *  <p>{@code subset} must be contained within the size set by
-     *  {@link #setResize} or the bounds of the image if setResize was not
-     *  called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
+     *  not called. Otherwise an {@link IllegalStateException} will be thrown by
      *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
      *
      *  <p>NOT intended as a replacement for
@@ -1353,7 +1416,7 @@
         float scale = (float) dstDensity / srcDensity;
         int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
         int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-        decoder.setResize(scaledWidth, scaledHeight);
+        decoder.setTargetSize(scaledWidth, scaledHeight);
         return dstDensity;
     }
 
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 5a8fa07..0d32075 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,8 +33,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
-    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
-    boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
+    int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+    int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
             out KeymasterCertificateChain chain);
     boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
 
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 2daf733..46a7fa8 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -246,6 +246,82 @@
     public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
 
     /**
+     * Indicates that a call to {@link #generateKeyPair} was successful.
+     * @hide
+     */
+    public static final int KEY_GEN_SUCCESS = 0;
+
+    /**
+     * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
+     * @hide
+     */
+    public static final int KEY_GEN_MISSING_ALIAS = 1;
+
+    /**
+     * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
+     * have been provided.
+     * @hide
+     */
+    public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
+
+    /**
+     * Algorithm not supported by {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
+
+    /**
+     * Invalid algorithm parameters when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
+
+    /**
+     * Keystore is not available when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
+
+    /**
+     * General failure while calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_FAILURE = 6;
+
+    /**
+     * Successful call to {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_SUCCESS = 0;
+
+    /**
+     * Attestation challenge missing when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
+
+    /**
+     * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
+     * permissions to get device identifiers.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
+
+    /**
+     * The underlying hardware does not support Device ID attestation or cannot attest to the
+     * identifiers that are stored on the device. This indicates permanent inability
+     * to get attestation records on the device.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
+
+    /**
+     * General failure when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_FAILURE = 4;
+
+    /**
      * Returns an {@code Intent} that can be used for credential
      * installation. The intent may be used without any extras, in
      * which case the user will be able to install credentials from
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1924bbe..1e2ebf8 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -66,6 +66,7 @@
     public static final int VALUE_CORRUPTED = 8;
     public static final int UNDEFINED_ACTION = 9;
     public static final int WRONG_PASSWORD = 10;
+    public static final int CANNOT_ATTEST_IDS = -66;
     public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
 
     /**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a444ac8..f43e719 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1724,6 +1724,9 @@
         dumpSetting(s, p,
                 Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS,
                 SecureSettingsProto.Backup.LOCAL_TRANSPORT_PARAMETERS);
+        dumpSetting(s, p,
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+                SecureSettingsProto.Backup.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
         p.end(backupToken);
 
         // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 402d9fdd..4b6ab64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,25 +18,21 @@
 
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
+import android.Manifest;
 import android.app.AppGlobals;
-import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.Context;
 import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.SnoozeCriterion;
@@ -46,10 +42,8 @@
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
-import android.Manifest;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.Dependency;
@@ -454,47 +448,44 @@
         return Ranking.VISIBILITY_NO_OVERRIDE;
     }
 
-    public boolean shouldSuppressFullScreenIntent(String key) {
+    public boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+    }
+
+    public boolean shouldSuppressPeek(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_PEEK);
+    }
+
+    public boolean shouldSuppressStatusBar(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_STATUS_BAR);
+    }
+
+    public boolean shouldSuppressAmbient(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_AMBIENT);
+    }
+
+    public boolean shouldSuppressNotificationList(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+    }
+
+    private boolean shouldSuppressVisualEffect(StatusBarNotification sbn, int effect) {
+        if (isExemptFromDndVisualSuppression(sbn)) {
+            return false;
+        }
+        String key = sbn.getKey();
         if (mRankingMap != null) {
             getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0;
+            return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
         }
         return false;
     }
 
-    public boolean shouldSuppressPeek(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_PEEK) != 0;
+    protected boolean isExemptFromDndVisualSuppression(StatusBarNotification sbn) {
+        if ((sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return true;
         }
-        return false;
-    }
-
-    public boolean shouldSuppressStatusBar(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
-        }
-        return false;
-    }
-
-    public boolean shouldSuppressAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_AMBIENT) != 0;
-        }
-        return false;
-    }
-
-    public boolean shouldSuppressNotificationList(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+        if (sbn.getNotification().isMediaNotification()) {
+            return true;
         }
         return false;
     }
@@ -620,11 +611,11 @@
             return true;
         }
 
-        if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn.getKey())) {
+        if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn)) {
             return true;
         }
 
-        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn.getKey())) {
+        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn)) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7a7cc99..45df450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -299,12 +299,12 @@
         updateNotifications();
     }
 
-    private boolean shouldSuppressFullScreenIntent(String key) {
+    private boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
         }
 
-        return mNotificationData.shouldSuppressFullScreenIntent(key);
+        return mNotificationData.shouldSuppressFullScreenIntent(sbn);
     }
 
     private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
@@ -690,7 +690,7 @@
         NotificationData.Entry shadeEntry = createNotificationViews(notification);
         boolean isHeadsUped = shouldPeek(shadeEntry);
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (shouldSuppressFullScreenIntent(key)) {
+            if (shouldSuppressFullScreenIntent(notification)) {
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
                 }
@@ -846,13 +846,13 @@
             return false;
         }
 
-        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn)) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
 
         // Peeking triggers an ambient display pulse, so disable peek is ambient is active
-        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn.getKey())) {
+        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn)) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 9063dea..b6a11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -150,7 +150,7 @@
 
         // showAmbient == show in shade but not shelf
         if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
-                entry.key)) {
+                entry.notification)) {
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 7ffca173..d527465 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -323,10 +323,10 @@
 
         if (notif != null) {
             mNotificationManager.notifyAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
-                    notif, UserHandle.ALL);
+                    notif, UserHandle.of(vol.getMountUserId()));
         } else {
             mNotificationManager.cancelAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
-                    UserHandle.ALL);
+                    UserHandle.of(vol.getMountUserId()));
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index c437021..5e27fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -35,6 +35,7 @@
 import android.app.NotificationChannel;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
 import android.os.Bundle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -61,6 +62,7 @@
     private static final int UID_NORMAL = 123;
     private static final int UID_ALLOW_DURING_SETUP = 456;
     private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+    private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
 
     private final StatusBarNotification mMockStatusBarNotification =
             mock(StatusBarNotification.class);
@@ -275,6 +277,7 @@
 
     @Test
     public void testShouldFilterHiddenNotifications() {
+        initStatusBarNotification(false);
         // setup
         when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
         when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
@@ -289,6 +292,33 @@
         assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
     }
 
+    @Test
+    public void testIsExemptFromDndVisualSuppression_foreground() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        n.flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+        assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_media() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        n = nb.build();
+        when(mMockStatusBarNotification.getNotification()).thenReturn(n);
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+        assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -318,6 +348,13 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+            } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), 255,
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
             } else {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8941b49..de112f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -853,11 +853,7 @@
             if (mSecurityPolicy.findA11yWindowInfoById(windowId) == null) {
                 return null;
             }
-            IBinder token = mGlobalWindowTokens.get(windowId);
-            if (token != null) {
-                return token;
-            }
-            return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+            return findWindowTokenLocked(windowId);
         }
     }
 
@@ -2527,6 +2523,14 @@
         getInteractionBridge().clearAccessibilityFocusNotLocked(windowId);
     }
 
+    private IBinder findWindowTokenLocked(int windowId) {
+        IBinder token = mGlobalWindowTokens.get(windowId);
+        if (token != null) {
+            return token;
+        }
+        return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+    }
+
     private int findWindowIdLocked(IBinder token) {
         final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
         if (globalIndex >= 0) {
@@ -2986,7 +2990,7 @@
                 // the accessibility layer reports which are windows
                 // that a sighted user can touch.
                 default: {
-                    return isRetrievalAllowingWindow(event.getWindowId());
+                    return isRetrievalAllowingWindowLocked(event.getWindowId());
                 }
             }
         }
@@ -3438,7 +3442,8 @@
 
         public boolean canGetAccessibilityNodeInfoLocked(
                 AbstractAccessibilityServiceConnection service, int windowId) {
-            return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
+            return canRetrieveWindowContentLocked(service)
+                    && isRetrievalAllowingWindowLocked(windowId);
         }
 
         public boolean canRetrieveWindowsLocked(AbstractAccessibilityServiceConnection service) {
@@ -3523,17 +3528,40 @@
                     || userId == UserHandle.USER_CURRENT_OR_SELF);
         }
 
-        private boolean isRetrievalAllowingWindow(int windowId) {
+        private boolean isRetrievalAllowingWindowLocked(int windowId) {
             // The system gets to interact with any window it wants.
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
                 return true;
             }
+            if (Binder.getCallingUid() == Process.SHELL_UID) {
+                if (!isShellAllowedToRetrieveWindowLocked(windowId)) {
+                    return false;
+                }
+            }
             if (windowId == mActiveWindowId) {
                 return true;
             }
             return findA11yWindowInfoById(windowId) != null;
         }
 
+        private boolean isShellAllowedToRetrieveWindowLocked(int windowId) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                IBinder windowToken = findWindowTokenLocked(windowId);
+                if (windowToken == null) {
+                    return false;
+                }
+                int userId = mWindowManagerService.getWindowOwnerUserId(windowToken);
+                if (userId == UserHandle.USER_NULL) {
+                    return false;
+                }
+                return !mUserManager.hasUserRestriction(
+                        UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) {
             return mA11yWindowInfoById.get(windowId);
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d4ecd8b..3253f2e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 
-import static com.android.server.autofill.Helper.bundleToString;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sPartitionMaxCount;
@@ -193,8 +192,7 @@
                 if (disabledBefore == disabledNow) {
                     // Nothing changed, do nothing.
                     if (sDebug) {
-                        Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
-                                + bundleToString(newRestrictions));
+                        Slog.d(TAG, "Autofill restriction did not change for user " + userId);
                         return;
                     }
                 }
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index a202aaf..78526f5 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -79,28 +79,6 @@
         throw new UnsupportedOperationException("contains static members only");
     }
 
-    static void append(StringBuilder builder, Bundle bundle) {
-        if (bundle == null || !sVerbose) {
-            builder.append("null");
-            return;
-        }
-        final Set<String> keySet = bundle.keySet();
-        builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
-        for (String key : keySet) {
-            final Object value = bundle.get(key);
-            builder.append(' ').append(key).append('=');
-            builder.append((value instanceof Object[])
-                    ? Arrays.toString((Objects[]) value) : value);
-        }
-        builder.append(']');
-    }
-
-    static String bundleToString(Bundle bundle) {
-        final StringBuilder builder = new StringBuilder();
-        append(builder, bundle);
-        return builder.toString();
-    }
-
     @Nullable
     static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
         if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 706fb1a..706a6ab 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2502,8 +2502,10 @@
         }
 
         pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
-        pw.print(prefix); pw.print("mClientState: "); pw.println(
-                Helper.bundleToString(mClientState));
+        if (mClientState != null) {
+            pw.print(prefix); pw.println("mClientState: "); pw.print(mClientState.getSize()); pw
+                .println(" items");
+        }
         pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
         pw.print(prefix); pw.print("mUrlBar: ");
         if (mUrlBar == null) {
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index 4de2c9b..49fa1cc 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -21,6 +21,7 @@
 import android.provider.Settings;
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
+import android.util.Slog;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -31,6 +32,8 @@
  * are represented as a comma-delimited key value list.
  */
 public class BackupAgentTimeoutParameters extends KeyValueSettingObserver {
+    private static final String TAG = "BackupAgentTimeout";
+
     @VisibleForTesting
     public static final String SETTING = Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS;
 
@@ -120,30 +123,50 @@
 
     public long getKvBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
+            }
             return mKvBackupAgentTimeoutMillis;
         }
     }
 
     public long getFullBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
+            }
             return mFullBackupAgentTimeoutMillis;
         }
     }
 
     public long getSharedBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(
+                        TAG,
+                        "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
+            }
             return mSharedBackupAgentTimeoutMillis;
         }
     }
 
     public long getRestoreAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
+            }
             return mRestoreAgentTimeoutMillis;
         }
     }
 
     public long getRestoreAgentFinishedTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(
+                        TAG,
+                        "getRestoreAgentFinishedTimeoutMillis(): "
+                                + mRestoreAgentFinishedTimeoutMillis);
+            }
             return mRestoreAgentFinishedTimeoutMillis;
         }
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..bd51af2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -215,13 +215,6 @@
     // Timeout interval for deciding that a bind or clear-data has taken too long
     private static final long TIMEOUT_INTERVAL = 10 * 1000;
 
-    // Timeout intervals for agent backup & restore operations
-    public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
-    public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
-    public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
-    public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
-    public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
-
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
     private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
@@ -232,6 +225,7 @@
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
     private BackupManagerConstants mConstants;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private Context mContext;
     private PackageManager mPackageManager;
     private IPackageManager mPackageManagerBinder;
@@ -315,6 +309,10 @@
         return mConstants;
     }
 
+    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+        return mAgentTimeoutParameters;
+    }
+
     public Context getContext() {
         return mContext;
     }
@@ -799,6 +797,10 @@
 
         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
 
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
         // spin up the backup/restore handler thread
         mBackupHandler = new BackupHandler(this, backupThread.getLooper());
 
@@ -3407,7 +3409,7 @@
             }
             mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
         }
         return mActiveRestoreSession;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 7b021c6..aabe7f6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -191,4 +191,7 @@
   void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
   IBackupManager getBackupManagerBinder();
+
+  // Gets access to the backup/restore agent timeout parameters.
+  BackupAgentTimeoutParameters getAgentTimeoutParameters();
 }
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 4755877..7f0030a 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -19,6 +19,7 @@
 import android.os.SELinux;
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import libcore.io.IoUtils;
@@ -59,6 +60,7 @@
     private ParcelFileDescriptor mSavedState;
     private ParcelFileDescriptor mBackupData;
     private ParcelFileDescriptor mNewState;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
             BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
@@ -81,6 +83,9 @@
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
         mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void backupOnePackage() throws IOException {
@@ -148,8 +153,9 @@
     // Return true on backup success, false otherwise
     private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
         try {
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // Start backup and wait for BackupManagerService to get callback for success or timeout
@@ -231,14 +237,14 @@
     }
 
     private void writeBackupData() throws IOException {
-
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
 
         ParcelFileDescriptor[] pipes = null;
         try {
             pipes = ParcelFileDescriptor.createPipe();
 
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // We will have to create a runnable that will read the manifest and backup data we
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 0582aba..5694659 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,9 +25,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -44,9 +41,11 @@
 import android.util.Slog;
 import android.util.StringBuilderPrinter;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
-import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.BufferedOutputStream;
@@ -75,6 +74,7 @@
     private final long mQuota;
     private final int mOpToken;
     private final int mTransportFlags;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     class FullBackupRunner implements Runnable {
 
@@ -137,8 +137,8 @@
                 final boolean isSharedStorage =
                         mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
                 final long timeout = isSharedStorage ?
-                        TIMEOUT_SHARED_BACKUP_INTERVAL :
-                        TIMEOUT_FULL_BACKUP_INTERVAL;
+                        mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                        mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
 
                 if (DEBUG) {
                     Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
@@ -180,6 +180,9 @@
         mQuota = quota;
         mOpToken = opToken;
         mTransportFlags = transportFlags;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public int preflightCheck() throws RemoteException {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index 40b6967..bc7d9fc 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -33,6 +32,8 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IObbBackupService;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
@@ -46,10 +47,14 @@
 
     private BackupManagerService backupManagerService;
     volatile IObbBackupService mService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public FullBackupObbConnection(BackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void establish() {
@@ -75,8 +80,10 @@
         try {
             pipes = ParcelFileDescriptor.createPipe();
             int token = backupManagerService.generateRandomIntegerToken();
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT);
+                    token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
             mService.backupObbs(pkg.packageName, pipes[1], token,
                     backupManagerService.getBackupManagerBinder());
             FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2c2dd85..a40afc3 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -22,7 +22,6 @@
 import static com.android.server.backup.BackupManagerService.OP_PENDING;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
@@ -43,7 +42,9 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.BackupManagerService;
@@ -146,6 +147,7 @@
     private volatile boolean mIsDoingBackup;
     private volatile boolean mCancelAll;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
             TransportClient transportClient,
@@ -167,6 +169,9 @@
         mUserInitiated = userInitiated;
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         if (backupManagerService.isBackupOperationInProgress()) {
             if (DEBUG) {
@@ -698,9 +703,11 @@
         @Override
         public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
             int result;
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
                 backupManagerService.prepareOperationTimeout(
-                        mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                        mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
                 backupManagerService.addBackupTrace("preflighting");
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
@@ -713,7 +720,7 @@
                 // timeout had been produced.  In case of a real backstop timeout, mResult
                 // will still contain the value it was constructed with, AGENT_ERROR, which
                 // intentionaly falls into the "just report failure" code.
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
 
                 long totalSize = mResult.get();
                 // If preflight timed out, mResult will contain error code as int.
@@ -769,8 +776,10 @@
 
         @Override
         public long getExpectedSizeOrErrorCode() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 return mResult.get();
             } catch (InterruptedException e) {
                 return BackupTransport.NO_MORE_DATA;
@@ -863,8 +872,10 @@
         // If preflight succeeded, returns positive number - preflight size,
         // otherwise return negative error code.
         long getPreflightResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
@@ -879,8 +890,10 @@
         }
 
         int getBackupResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 136fada..69f08ae 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 
 import android.app.backup.RestoreSet;
 import android.content.Intent;
@@ -33,7 +32,9 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
@@ -81,10 +82,14 @@
     public static final int MSG_OP_COMPLETE = 21;
 
     private final BackupManagerService backupManagerService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
         super(looper);
         this.backupManagerService = backupManagerService;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void handleMessage(Message msg) {
@@ -322,7 +327,8 @@
 
                     // Done: reset the session timeout clock
                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
+                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+                            mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
 
                     params.listener.onFinished(callerLogString);
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 11394e66..ac605b1 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -24,7 +24,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 
@@ -55,8 +54,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
@@ -142,6 +143,7 @@
     private boolean mFinished;
     private final boolean mUserInitiated;
     private final boolean mNonIncremental;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     private volatile boolean mCancelAll;
 
@@ -162,6 +164,9 @@
         mPendingFullBackups = pendingFullBackups;
         mUserInitiated = userInitiated;
         mNonIncremental = nonIncremental;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +716,10 @@
 
             // Initiate the target's backup pass
             backupManagerService.addBackupTrace("setting timeout");
+            long kvBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                    mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
             backupManagerService.addBackupTrace("calling agent doBackup()");
 
             agent.doBackup(
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4f3a9d..a8c7ce6 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -18,10 +18,11 @@
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 
@@ -37,18 +38,24 @@
     private BackupManagerService backupManagerService;
     final CountDownLatch mLatch;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
             int currentOpToken) {
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
         mCurrentOpToken = currentOpToken;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     void await() {
         boolean latched = false;
+        long fullBackupAgentTimeoutMillis =
+                mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
         try {
-            latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+            latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             Slog.w(TAG, "Interrupted!");
         }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index c1a1c1d..6bc7530 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -40,13 +37,17 @@
 import android.content.pm.Signature;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -56,8 +57,11 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Full restore engine, used by both adb restore and transport-based full restore.
@@ -121,6 +125,8 @@
 
     final int mEphemeralOpToken;
 
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+
     public FullRestoreEngine(BackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
@@ -135,6 +141,9 @@
         mAllowObbs = allowObbs;
         mBuffer = new byte[32 * 1024];
         mBytes = 0;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public IBackupAgent getAgent() {
@@ -320,12 +329,17 @@
                                             pkg, 0);
 
                             // If we haven't sent any data to this app yet, we probably
-                            // need to clear it first.  Check that.
+                            // need to clear it first. Check that.
                             if (!mClearedPackages.contains(pkg)) {
-                                // apps with their own backup agents are
-                                // responsible for coherently managing a full
-                                // restore.
-                                if (mTargetApp.backupAgentName == null) {
+                                // Apps with their own backup agents are responsible for coherently
+                                // managing a full restore.
+                                // In some rare cases they can't, especially in case of deferred
+                                // restore. In this case check whether this app should be forced to
+                                // clear up.
+                                // TODO: Fix this properly with manifest parameter.
+                                boolean forceClear = shouldForceClearAppDataOnFullRestore(
+                                        mTargetApp.packageName);
+                                if (mTargetApp.backupAgentName == null || forceClear) {
                                     if (DEBUG) {
                                         Slog.d(TAG,
                                                 "Clearing app data preparatory to full restore");
@@ -381,8 +395,8 @@
                         long toCopy = info.size;
                         final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
                         final long timeout = isSharedStorage ?
-                                TIMEOUT_SHARED_BACKUP_INTERVAL :
-                                TIMEOUT_RESTORE_INTERVAL;
+                                mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(token,
                                     timeout,
@@ -623,6 +637,24 @@
         return true;
     }
 
+    /**
+     * Returns whether the package is in the list of the packages for which clear app data should
+     * be called despite the fact that they have backup agent.
+     *
+     * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+     */
+    private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
+        String packageListString = Settings.Secure.getString(
+                mBackupManagerService.getContext().getContentResolver(),
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+        if (TextUtils.isEmpty(packageListString)) {
+            return false;
+        }
+
+        List<String> packages = Arrays.asList(packageListString.split(";"));
+        return packages.contains(packageName);
+    }
+
     void sendOnRestorePackage(String name) {
         if (mObserver != null) {
             try {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index dacde0b..77163d3 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -28,8 +26,8 @@
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -48,7 +46,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -101,6 +101,7 @@
     private byte[] mWidgetData = null;
 
     private long mBytes;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // Runner that can be placed on a separate thread to do in-process invocation
     // of the "restore finished" API asynchronously.  Used by adb restore.
@@ -155,6 +156,9 @@
         mAgentPackage = null;
         mTargetApp = null;
         mObbConnection = new FullBackupObbConnection(backupManagerService);
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         // Which packages we've already wiped data on.  We prepopulate this
         // with a whitelist of packages known to be unclearable.
@@ -643,9 +647,11 @@
                     if (okay) {
                         boolean agentSuccess = true;
                         long toCopy = info.size;
+                        long restoreAgentTimeoutMillis =
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(
-                                    token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT);
+                                    token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
 
                             if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
                                 if (DEBUG) {
@@ -820,10 +826,12 @@
                 // In the adb restore case, we do restore-finished here
                 if (doRestoreFinished) {
                     final int token = mBackupManagerService.generateRandomIntegerToken();
+                    long fullBackupAgentTimeoutMillis =
+                            mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
                     final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
                             mBackupManagerService, token);
                     mBackupManagerService.prepareOperationTimeout(
-                            token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT);
+                            token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
                     if (mTargetApp.processName.equals("system")) {
                         if (MORE_DEBUG) {
                             Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 4b467e5..12d72d8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_RESTORE_FINISHED_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -56,9 +53,11 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.PackageManagerBackupAgent;
@@ -160,6 +159,7 @@
     ParcelFileDescriptor mNewState;
 
     private final int mEphemeralOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
     // about releasing it.
@@ -190,6 +190,9 @@
         mFinished = false;
         mDidLaunch = false;
         mListener = listener;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         if (targetPackage != null) {
             // Single package restore
@@ -760,8 +763,9 @@
             // Kick off the restore, checking for hung agents.  The timeout or
             // the operationComplete() callback will schedule the next step,
             // so we do not do that here.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT);
+                    mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                     mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
         } catch (Exception e) {
@@ -813,9 +817,11 @@
             Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
         }
         try {
+            long restoreAgentFinishedTimeoutMillis =
+                    mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
             backupManagerService
                     .prepareOperationTimeout(mEphemeralOpToken,
-                            TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+                            restoreAgentFinishedTimeoutMillis, this,
                             OP_TYPE_RESTORE_WAIT);
             mAgent.doRestoreFinished(mEphemeralOpToken,
                     backupManagerService.getBackupManagerBinder());
@@ -1109,9 +1115,10 @@
         } else {
             // We were invoked via an active restore session, not by the Package
             // Manager, so start up the session timeout again.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
                     MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    restoreAgentTimeoutMillis);
         }
 
         // Kick off any work that may be needed regarding app widget restores
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 30125f8..77d7c3c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Binder;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
@@ -100,7 +99,6 @@
     private final RecoverableKeyGenerator mRecoverableKeyGenerator;
     private final RecoverySnapshotStorage mSnapshotStorage;
     private final PlatformKeyManager mPlatformKeyManager;
-    private final KeyStore mKeyStore;
     private final ApplicationKeyStorage mApplicationKeyStorage;
 
     /**
@@ -126,11 +124,10 @@
 
             mInstance = new RecoverableKeyStoreManager(
                     context.getApplicationContext(),
-                    keystore,
                     db,
                     new RecoverySessionStorage(),
                     Executors.newSingleThreadExecutor(),
-                    new RecoverySnapshotStorage(),
+                    RecoverySnapshotStorage.newInstance(),
                     new RecoverySnapshotListenersStorage(),
                     platformKeyManager,
                     applicationKeyStorage);
@@ -141,7 +138,6 @@
     @VisibleForTesting
     RecoverableKeyStoreManager(
             Context context,
-            KeyStore keystore,
             RecoverableKeyStoreDb recoverableKeyStoreDb,
             RecoverySessionStorage recoverySessionStorage,
             ExecutorService executorService,
@@ -150,7 +146,6 @@
             PlatformKeyManager platformKeyManager,
             ApplicationKeyStorage applicationKeyStorage) {
         mContext = context;
-        mKeyStore = keystore;
         mDatabase = recoverableKeyStoreDb;
         mRecoverySessionStorage = recoverySessionStorage;
         mExecutorService = executorService;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index dcaa0b4..f789155 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -23,6 +23,9 @@
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
@@ -128,6 +131,11 @@
                     }
                     break;
 
+                case TAG_BACKEND_PUBLIC_KEY:
+                    builder.setTrustedHardwarePublicKey(
+                            readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY));
+                    break;
+
                 case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST:
                     builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser));
                     break;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index ee8b2cf..ff30ecd 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -35,6 +35,7 @@
     static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial";
     static final String TAG_SERVER_PARAMS = "serverParams";
     static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath";
+    static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey";
 
     static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST =
             "keyChainProtectionParamsList";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index f817a8f..17a16bf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -24,6 +24,9 @@
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
@@ -159,6 +162,10 @@
         writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams());
         writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH,
                 keyChainSnapshot.getTrustedHardwareCertPath());
+        if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) {
+            writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY,
+                    keyChainSnapshot.getTrustedHardwarePublicKey());
+        }
     }
 
     private static void writePropertyTag(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 3f93cc6..c02b103 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,13 +17,28 @@
 package com.android.server.locksettings.recoverablekeystore.storage;
 
 import android.annotation.Nullable;
+import android.os.Environment;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotDeserializer;
+import com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotParserException;
+import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.util.Locale;
 
 /**
- * In-memory storage for recovery snapshots.
+ * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage.
  *
  * <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if
  * the recoverable keystore has been mutated since the previous snapshot. This class stores only the
@@ -33,14 +48,46 @@
  * {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread.
  */
 public class RecoverySnapshotStorage {
+
+    private static final String TAG = "RecoverySnapshotStorage";
+
+    private static final String ROOT_PATH = "system";
+    private static final String STORAGE_PATH = "recoverablekeystore/snapshots/";
+
     @GuardedBy("this")
     private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>();
 
+    private final File rootDirectory;
+
+    /**
+     * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots.
+     *
+     * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed
+     * by the same in-memory store.
+     */
+    public static RecoverySnapshotStorage newInstance() {
+        return new RecoverySnapshotStorage(
+                new File(Environment.getDataDirectory(), ROOT_PATH));
+    }
+
+    @VisibleForTesting
+    public RecoverySnapshotStorage(File rootDirectory) {
+        this.rootDirectory = rootDirectory;
+    }
+
     /**
      * Sets the latest {@code snapshot} for the recovery agent {@code uid}.
      */
     public synchronized void put(int uid, KeyChainSnapshot snapshot) {
         mSnapshotByUid.put(uid, snapshot);
+
+        try {
+            writeToDisk(uid, snapshot);
+        } catch (IOException | CertificateEncodingException e) {
+            Log.e(TAG,
+                    String.format(Locale.US, "Error persisting snapshot for %d to disk", uid),
+                    e);
+        }
     }
 
     /**
@@ -48,7 +95,17 @@
      */
     @Nullable
     public synchronized KeyChainSnapshot get(int uid) {
-        return mSnapshotByUid.get(uid);
+        KeyChainSnapshot snapshot = mSnapshotByUid.get(uid);
+        if (snapshot != null) {
+            return snapshot;
+        }
+
+        try {
+            return readFromDisk(uid);
+        } catch (IOException | KeyChainSnapshotParserException e) {
+            Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e);
+            return null;
+        }
     }
 
     /**
@@ -56,5 +113,66 @@
      */
     public synchronized void remove(int uid) {
         mSnapshotByUid.remove(uid);
+        getSnapshotFile(uid).delete();
+    }
+
+    /**
+     * Writes the snapshot for recovery agent {@code uid} to disk.
+     *
+     * @throws IOException if an IO error occurs writing to disk.
+     */
+    private void writeToDisk(int uid, KeyChainSnapshot snapshot)
+            throws IOException, CertificateEncodingException {
+        File snapshotFile = getSnapshotFile(uid);
+
+        try (
+            FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)
+        ) {
+            KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
+        } catch (IOException | CertificateEncodingException e) {
+            // If we fail to write the latest snapshot, we should delete any older snapshot that
+            // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
+            snapshotFile.delete();
+            throw e;
+        }
+    }
+
+    /**
+     * Reads the last snapshot for recovery agent {@code uid} from disk.
+     *
+     * @return The snapshot, or null if none existed.
+     * @throws IOException if an IO error occurs reading from disk.
+     */
+    @Nullable
+    private KeyChainSnapshot readFromDisk(int uid)
+            throws IOException, KeyChainSnapshotParserException {
+        File snapshotFile = getSnapshotFile(uid);
+
+        try (
+            FileInputStream fileInputStream = new FileInputStream(snapshotFile)
+        ) {
+            return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
+        } catch (IOException | KeyChainSnapshotParserException e) {
+            // If we fail to read the latest snapshot, we should delete it in case it is in some way
+            // corrupted. We can regenerate snapshots anyway.
+            snapshotFile.delete();
+            throw e;
+        }
+    }
+
+    private File getSnapshotFile(int uid) {
+        File folder = getStorageFolder();
+        String fileName = getSnapshotFileName(uid);
+        return new File(folder, fileName);
+    }
+
+    private String getSnapshotFileName(int uid) {
+        return String.format(Locale.US, "%d.xml", uid);
+    }
+
+    private File getStorageFolder() {
+        File folder = new File(rootDirectory, STORAGE_PATH);
+        folder.mkdirs();
+        return folder;
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 210857e..31b0461 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2149,7 +2149,8 @@
                 final NotificationChannel channel = channels.get(i);
                 Preconditions.checkNotNull(channel, "channel in list is null");
                 mRankingHelper.createNotificationChannel(pkg, uid, channel,
-                        true /* fromTargetApp */);
+                        true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
+                                pkg, UserHandle.getUserId(uid)));
                 mListeners.notifyNotificationChannelChanged(pkg,
                         UserHandle.getUserHandleForUid(uid),
                         mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
@@ -4691,11 +4692,12 @@
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
         boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
-        // do not play notifications if there is a user of exclusive audio focus
-        // or the device is in vibrate mode
-        if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
-                != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
-                AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
+        // play notifications if there is no user of exclusive audio focus
+        // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+        //   VIBRATE ringer mode)
+        if (!mAudioManager.isAudioFocusExclusive()
+                && (mAudioManager.getStreamVolume(
+                        AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1b0bf2..af64683 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -38,7 +38,7 @@
     ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid, boolean includeDeleted, boolean includeNonGrouped);
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
-            boolean fromTargetApp);
+            boolean fromTargetApp, boolean hasDndAccess);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
     NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f163113..98d5c9a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -569,7 +569,7 @@
 
     @Override
     public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
-            boolean fromTargetApp) {
+            boolean fromTargetApp, boolean hasDndAccess) {
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(channel);
         Preconditions.checkNotNull(channel.getId());
@@ -610,8 +610,9 @@
                 existing.setImportance(channel.getImportance());
             }
 
-            // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
-            if (existing.getUserLockedFields() == 0 & isSystemApp) {
+            // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+            // fields on the channel yet
+            if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
                 existing.setBypassDnd(channel.canBypassDnd());
             }
 
@@ -624,7 +625,7 @@
         }
 
         // Reset fields that apps aren't allowed to set.
-        if (fromTargetApp && !isSystemApp) {
+        if (fromTargetApp && !(isSystemApp || hasDndAccess)) {
             channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
         }
         if (fromTargetApp) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6bceda5..6686b80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -419,4 +419,10 @@
      * @see android.view.IWindowManager#lockNow
      */
     public abstract void lockNow();
+
+    /**
+     * Return the user that owns the given window, {@link android.os.UserHandle#USER_NULL} if
+     * the window token is not found.
+     */
+    public abstract int getWindowOwnerUserId(IBinder windowToken);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b5c006..c8b4f88 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7351,6 +7351,17 @@
         public void lockNow() {
             WindowManagerService.this.lockNow(null);
         }
+
+        @Override
+        public int getWindowOwnerUserId(IBinder token) {
+            synchronized (mWindowMap) {
+                WindowState window = mWindowMap.get(token);
+                if (window != null) {
+                    return UserHandle.getUserId(window.mOwnerUid);
+                }
+                return UserHandle.USER_NULL;
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5cfba22..02cd3b6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5533,10 +5533,11 @@
                         .setAttestationChallenge(null)
                         .build();
 
-                final boolean generationResult = keyChain.generateKeyPair(algorithm,
+                final int generationResult = keyChain.generateKeyPair(algorithm,
                     new ParcelableKeyGenParameterSpec(noAttestationSpec));
-                if (!generationResult) {
-                    Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+                if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
+                    Log.e(LOG_TAG, String.format(
+                            "KeyChain failed to generate a keypair, error %d.", generationResult));
                     return false;
                 }
 
@@ -5549,12 +5550,17 @@
 
                 final byte[] attestationChallenge = keySpec.getAttestationChallenge();
                 if (attestationChallenge != null) {
-                    final boolean attestationResult = keyChain.attestKey(
+                    final int attestationResult = keyChain.attestKey(
                             alias, attestationChallenge, attestationUtilsFlags, attestationChain);
-                    if (!attestationResult) {
+                    if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
                         Log.e(LOG_TAG, String.format(
-                                "Attestation for %s failed, deleting key.", alias));
+                                "Attestation for %s failed (rc=%d), deleting key.",
+                                alias, attestationResult));
                         keyChain.removeKeyPair(alias);
+                        if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
+                            throw new UnsupportedOperationException(
+                                    "Device does not support Device ID attestation.");
+                        }
                         return false;
                     }
                 }
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index f603a09..fa41220 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -147,6 +147,15 @@
 
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
+
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
+        // We need to mock BMS timeout parameters before initializing the BackupHandler since
+        // the constructor of BackupHandler relies on the timeout parameters.
+        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
         mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
 
         mBackupManager = spy(FakeIBackupManager.class);
@@ -157,7 +166,8 @@
                 mTransportManager,
                 packageManager,
                 mBackupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
         when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
         when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 03792b1..92d6bbd 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -41,12 +41,14 @@
 import android.app.backup.RestoreSet;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -115,6 +117,15 @@
 
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
+
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
+        // We need to mock BMS timeout parameters before initializing the BackupHandler since
+        // the constructor of BackupHandler relies on it.
+        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
         BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
 
         mWakeLock = createBackupWakeLock(application);
@@ -125,7 +136,8 @@
                 mTransportManager,
                 application.getPackageManager(),
                 backupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
     }
 
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index c210fde..5a886e3 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -28,6 +28,7 @@
 import android.os.PowerManager;
 import android.util.SparseArray;
 
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -43,7 +44,8 @@
             TransportManager transportManager,
             PackageManager packageManager,
             BackupHandler backupHandler,
-            PowerManager.WakeLock wakeLock) {
+            PowerManager.WakeLock wakeLock,
+            BackupAgentTimeoutParameters agentTimeoutParameters) {
         when(backupManagerService.getContext()).thenReturn(context);
         when(backupManagerService.getTransportManager()).thenReturn(transportManager);
         when(backupManagerService.getPackageManager()).thenReturn(packageManager);
@@ -53,6 +55,7 @@
         when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
         when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
         when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+        when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
     }
 
     public static PowerManager.WakeLock createBackupWakeLock(Application application) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 9ae45ea..81a73efd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.FileUtils;
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
@@ -49,7 +50,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import android.util.Log;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
 
@@ -72,6 +72,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeySyncTaskTest {
+
+    private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
@@ -117,7 +120,7 @@
                 TEST_ROOT_CERT_ALIAS);
         mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
                 TEST_ROOT_CERT_ALIAS);
-        mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+        mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir());
 
         mKeySyncTask = new KeySyncTask(
                 mRecoverableKeyStoreDb,
@@ -139,6 +142,10 @@
     public void tearDown() {
         mRecoverableKeyStoreDb.close();
         mDatabaseFile.delete();
+
+        File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
+                SNAPSHOT_TOP_LEVEL_DIRECTORY);
+        FileUtils.deleteContentsAndDir(file);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index f5f5027..18a3885 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -189,7 +189,6 @@
 
         mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
                 mMockContext,
-                KeyStore.getInstance(),
                 mRecoverableKeyStoreDb,
                 mRecoverySessionStorage,
                 Executors.newSingleThreadExecutor(),
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
index 6c2958e..2f4da86 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -45,6 +45,7 @@
     private static final int MAX_ATTEMPTS = 21;
     private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 };
     private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 };
+    private static final byte[] PUBLIC_KEY_BLOB = new byte[] { 6, 6, 6, 6, 6, 6, 7 };
     private static final CertPath CERT_PATH = TestData.CERT_PATH_1;
     private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
     private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
@@ -93,6 +94,11 @@
     }
 
     @Test
+    public void roundTrip_persistsBackendPublicKey() throws Exception {
+        assertThat(roundTrip().getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY_BLOB);
+    }
+
+    @Test
     public void roundTrip_persistsParamsList() throws Exception {
         assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1);
     }
@@ -163,6 +169,12 @@
         assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES);
     }
 
+    @Test
+    public void serialize_doesNotThrowForNullPublicKey() throws Exception {
+        KeyChainSnapshotSerializer.serialize(
+                createTestKeyChainSnapshotNoPublicKey(), new ByteArrayOutputStream());
+    }
+
     private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
         return roundTrip().getWrappedApplicationKeys();
     }
@@ -180,6 +192,41 @@
     }
 
     private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+        return new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+                .setWrappedApplicationKeys(createKeys())
+                .setTrustedHardwareCertPath(CERT_PATH)
+                .setTrustedHardwarePublicKey(PUBLIC_KEY_BLOB)
+                .build();
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshotNoPublicKey() throws Exception {
+        return new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+                .setWrappedApplicationKeys(createKeys())
+                .setTrustedHardwareCertPath(CERT_PATH)
+                .build();
+    }
+
+    private static List<WrappedApplicationKey> createKeys() {
+        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+        return keyList;
+    }
+
+    private static List<KeyChainProtectionParams> createKeyChainProtectionParamsList() {
         KeyDerivationParams keyDerivationParams =
                 KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
         KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
@@ -191,22 +238,7 @@
         ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
                 new ArrayList<>(1);
         keyChainProtectionParamsList.add(keyChainProtectionParams);
-
-        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
-        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
-        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
-        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
-
-        return new KeyChainSnapshot.Builder()
-                .setCounterId(COUNTER_ID)
-                .setSnapshotVersion(SNAPSHOT_VERSION)
-                .setServerParams(SERVER_PARAMS)
-                .setMaxAttempts(MAX_ATTEMPTS)
-                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
-                .setKeyChainProtectionParams(keyChainProtectionParamsList)
-                .setWrappedApplicationKeys(keyList)
-                .setTrustedHardwareCertPath(CERT_PATH)
-                .build();
+        return keyChainProtectionParamsList;
     }
 
     private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index c772956..ad14c3a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -1,27 +1,82 @@
 package com.android.server.locksettings.recoverablekeystore.storage;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
+import android.os.FileUtils;
+import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.server.locksettings.recoverablekeystore.TestData;
 
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class RecoverySnapshotStorageTest {
-    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
-            createMinimalKeyChainSnapshot();
+    private static final int COUNTER_ID = 432546;
+    private static final int MAX_ATTEMPTS = 10;
+    private static final byte[] SERVER_PARAMS = new byte[] { 12, 8, 2, 4, 15, 64 };
+    private static final byte[] KEY_BLOB = new byte[] { 124, 56, 53, 99, 0, 0, 1 };
+    private static final CertPath CERT_PATH = TestData.CERT_PATH_2;
+    private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+    private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PATTERN;
+    private static final byte[] SALT = new byte[] { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+    private static final int MEMORY_DIFFICULTY = 12;
+    private static final byte[] SECRET = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 };
 
-    private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+    private static final String TEST_KEY_1_ALIAS = "alias1";
+    private static final byte[] TEST_KEY_1_BYTES = new byte[] { 100, 32, 43, 66, 77, 88 };
+
+    private static final String TEST_KEY_2_ALIAS = "alias11";
+    private static final byte[] TEST_KEY_2_BYTES = new byte[] { 100, 0, 0, 99, 33, 11 };
+
+    private static final String TEST_KEY_3_ALIAS = "alias111";
+    private static final byte[] TEST_KEY_3_BYTES = new byte[] { 1, 1, 1, 0, 2, 8, 100 };
+
+    private static final int TEST_UID = 1000;
+    private static final String SNAPSHOT_DIRECTORY = "recoverablekeystore/snapshots";
+    private static final String SNAPSHOT_FILE_PATH = "1000.xml";
+    private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
+    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+            createTestKeyChainSnapshot(1);
+
+    private Context mContext;
+    private RecoverySnapshotStorage mRecoverySnapshotStorage;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mRecoverySnapshotStorage = new RecoverySnapshotStorage(mContext.getFilesDir());
+    }
+
+    @After
+    public void tearDown() {
+        File file = new File(mContext.getFilesDir(), SNAPSHOT_TOP_LEVEL_DIRECTORY);
+        FileUtils.deleteContentsAndDir(file);
+    }
 
     @Test
     public void get_isNullForNonExistentSnapshot() {
@@ -30,37 +85,153 @@
 
     @Test
     public void get_returnsSetSnapshot() {
-        int userId = 1000;
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
 
-        mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-
-        assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(userId));
+        assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(TEST_UID));
     }
 
     @Test
-    public void remove_removesSnapshots() {
-        int userId = 1000;
+    public void get_readsFromDiskIfNoneInMemory() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+        RecoverySnapshotStorage storage = new RecoverySnapshotStorage(mContext.getFilesDir());
 
-        mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-        mRecoverySnapshotStorage.remove(userId);
-
-        assertNull(mRecoverySnapshotStorage.get(1000));
+        assertKeyChainSnapshotsAreEqual(MINIMAL_KEYCHAIN_SNAPSHOT, storage.get(TEST_UID));
     }
 
-    private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+    @Test
+    public void get_deletesFileIfItIsInvalidSnapshot() throws Exception {
+        File folder = new File(mContext.getFilesDir(), SNAPSHOT_DIRECTORY);
+        folder.mkdirs();
+        File file = new File(folder, SNAPSHOT_FILE_PATH);
+        byte[] fileContents = "<keyChainSnapshot></keyChainSnapshot>".getBytes(
+                StandardCharsets.UTF_8);
+        Files.write(fileContents, file);
+        assertTrue(file.exists());
+
+        assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+
+        assertFalse(file.exists());
+    }
+
+    @Test
+    public void put_overwritesOldFiles() {
+        int snapshotVersion = 2;
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        mRecoverySnapshotStorage.put(TEST_UID, createTestKeyChainSnapshot(snapshotVersion));
+
+        KeyChainSnapshot snapshot = new RecoverySnapshotStorage(mContext.getFilesDir())
+                .get(TEST_UID);
+        assertEquals(snapshotVersion, snapshot.getSnapshotVersion());
+    }
+
+    @Test
+    public void put_doesNotThrowIfCannotCreateFiles() throws Exception {
+        File evilFile = new File(mContext.getFilesDir(), "recoverablekeystore");
+        Files.write(new byte[] { 1 }, evilFile);
+
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+    }
+
+    @Test
+    public void remove_removesSnapshotsFromMemory() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+        mRecoverySnapshotStorage.remove(TEST_UID);
+
+        assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+    }
+
+    @Test
+    public void remove_removesSnapshotsFromDisk() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        new RecoverySnapshotStorage(mContext.getFilesDir()).remove(TEST_UID);
+
+        assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+    }
+
+    private void assertKeyChainSnapshotsAreEqual(KeyChainSnapshot a, KeyChainSnapshot b) {
+        assertEquals(b.getCounterId(), a.getCounterId());
+        assertEquals(b.getSnapshotVersion(), a.getSnapshotVersion());
+        assertArrayEquals(b.getServerParams(), a.getServerParams());
+        assertEquals(b.getMaxAttempts(), a.getMaxAttempts());
+        assertArrayEquals(b.getEncryptedRecoveryKeyBlob(), a.getEncryptedRecoveryKeyBlob());
+        assertEquals(b.getTrustedHardwareCertPath(), a.getTrustedHardwareCertPath());
+
+        List<WrappedApplicationKey> aKeys = a.getWrappedApplicationKeys();
+        List<WrappedApplicationKey> bKeys = b.getWrappedApplicationKeys();
+        assertEquals(bKeys.size(), aKeys.size());
+        for (int i = 0; i < aKeys.size(); i++) {
+            assertWrappedApplicationKeysAreEqual(aKeys.get(i), bKeys.get(i));
+        }
+
+        List<KeyChainProtectionParams> aParams = a.getKeyChainProtectionParams();
+        List<KeyChainProtectionParams> bParams = b.getKeyChainProtectionParams();
+        assertEquals(bParams.size(), aParams.size());
+        for (int i = 0; i < aParams.size(); i++) {
+            assertKeyChainProtectionParamsAreEqual(aParams.get(i), bParams.get(i));
+        }
+    }
+
+    private void assertWrappedApplicationKeysAreEqual(
+            WrappedApplicationKey a, WrappedApplicationKey b) {
+        assertEquals(b.getAlias(), a.getAlias());
+        assertArrayEquals(b.getEncryptedKeyMaterial(), a.getEncryptedKeyMaterial());
+    }
+
+    private void assertKeyChainProtectionParamsAreEqual(
+            KeyChainProtectionParams a, KeyChainProtectionParams b) {
+        assertEquals(b.getUserSecretType(), a.getUserSecretType());
+        assertEquals(b.getLockScreenUiFormat(), a.getLockScreenUiFormat());
+        assertKeyDerivationParamsAreEqual(a.getKeyDerivationParams(), b.getKeyDerivationParams());
+    }
+
+    private void assertKeyDerivationParamsAreEqual(KeyDerivationParams a, KeyDerivationParams b) {
+        assertEquals(b.getAlgorithm(), a.getAlgorithm());
+        assertEquals(b.getMemoryDifficulty(), a.getMemoryDifficulty());
+        assertArrayEquals(b.getSalt(), a.getSalt());
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshot(int snapshotVersion) {
+        KeyDerivationParams keyDerivationParams =
+                KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+        KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+                .setKeyDerivationParams(keyDerivationParams)
+                .setUserSecretType(SECRET_TYPE)
+                .setLockScreenUiFormat(LOCK_SCREEN_UI)
+                .setSecret(SECRET)
+                .build();
+        ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+                new ArrayList<>(1);
+        keyChainProtectionParamsList.add(keyChainProtectionParams);
+
+        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+
         try {
             return new KeyChainSnapshot.Builder()
-                    .setCounterId(1)
-                    .setSnapshotVersion(1)
-                    .setServerParams(new byte[0])
-                    .setMaxAttempts(10)
-                    .setEncryptedRecoveryKeyBlob(new byte[0])
-                    .setKeyChainProtectionParams(new ArrayList<>())
-                    .setWrappedApplicationKeys(new ArrayList<>())
-                    .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+                    .setCounterId(COUNTER_ID)
+                    .setSnapshotVersion(snapshotVersion)
+                    .setServerParams(SERVER_PARAMS)
+                    .setMaxAttempts(MAX_ATTEMPTS)
+                    .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                    .setKeyChainProtectionParams(keyChainProtectionParamsList)
+                    .setWrappedApplicationKeys(keyList)
+                    .setTrustedHardwareCertPath(CERT_PATH)
                     .build();
         } catch (CertificateException e) {
             throw new RuntimeException(e);
         }
     }
+
+    private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(alias)
+                .setEncryptedKeyMaterial(bytes)
+                .build();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 09d88fd..0815876 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -367,8 +367,8 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
 
         mHelper.setShowBadge(PKG, UID, true);
 
@@ -427,10 +427,10 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, false);
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
+        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
 
         mHelper.setShowBadge(PKG, UID, true);
 
@@ -481,7 +481,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
@@ -507,7 +507,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -528,7 +528,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -569,7 +569,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(null, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -593,9 +593,9 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
         mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
@@ -701,7 +701,7 @@
     @Test
     public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
 
@@ -721,7 +721,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
 
         loadStreamXml(baos, false);
 
@@ -734,36 +734,39 @@
         mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
 
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
     }
 
     @Test
     public void testCreateChannel_badImportance() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
     }
 
 
@@ -777,7 +780,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
+        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
 
         // same id, try to update all fields
         final NotificationChannel channel2 =
@@ -824,7 +827,7 @@
     public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
         final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
+        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
         assertTrue(mHelper.canShowBadge(PKG, UID));
         assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
@@ -865,7 +868,7 @@
         }
         channel.lockFields(lockMask);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel savedChannel =
                 mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -894,7 +897,7 @@
         }
         channel.lockFields(lockMask);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel savedChannel =
                 mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -920,7 +923,7 @@
 
     @Test
     public void testLockFields_soundAndVibration() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setSound(new Uri.Builder().scheme("test").build(),
@@ -944,7 +947,7 @@
 
     @Test
     public void testLockFields_vibrationAndLights() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setVibrationPattern(new long[]{7945, 46 ,246});
@@ -964,7 +967,7 @@
 
     @Test
     public void testLockFields_lightsAndImportance() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setLightColor(Color.GREEN);
@@ -984,7 +987,7 @@
 
     @Test
     public void testLockFields_visibilityAndDndAndBadge() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
                         .getUserLockedFields());
@@ -1029,7 +1032,7 @@
         channel.enableVibration(true);
         channel.setVibrationPattern(new long[]{100, 67, 145, 156});
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
         // Does not return deleted channel
@@ -1058,8 +1061,8 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
         channelMap.put(channel2.getId(), channel2);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
@@ -1091,9 +1094,9 @@
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel3 =
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
         mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
@@ -1109,14 +1112,14 @@
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel.setVibrationPattern(vibration);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
         NotificationChannel newChannel = new NotificationChannel(
                 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
         newChannel.setVibrationPattern(new long[]{100});
 
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
 
         // No long deleted, using old settings
         compareChannels(channel,
@@ -1128,7 +1131,7 @@
         assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
         assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
 
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
         assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
     }
 
@@ -1136,7 +1139,7 @@
     public void testCreateChannel_defaultChannelId() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
-                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true);
+                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
             fail("Allowed to create default channel");
         } catch (IllegalArgumentException e) {
             // pass
@@ -1150,13 +1153,13 @@
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel.setVibrationPattern(vibration);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel newChannel = new NotificationChannel(
                 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
         newChannel.setVibrationPattern(new long[]{100});
 
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
 
         // Old settings not overridden
         compareChannels(channel,
@@ -1169,7 +1172,7 @@
         final NotificationChannel channel = new NotificationChannel("id2", "name2",
                  NotificationManager.IMPORTANCE_DEFAULT);
         channel.setSound(sound, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         assertEquals(sound, mHelper.getNotificationChannel(
                 PKG, UID, channel.getId(), false).getSound());
     }
@@ -1181,8 +1184,8 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
 
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
 
         mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
 
@@ -1205,9 +1208,9 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
         mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
-        mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true);
-        mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true);
-        mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true);
+        mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
+        mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
 
         mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
 
@@ -1264,14 +1267,14 @@
         // Deleted
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
 
         assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
 
         // Not deleted
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
         assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
@@ -1302,7 +1305,7 @@
     @Test
     public void testOnPackageChange_downgradeTargetSdk() throws Exception {
         // create channel as api 26
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
 
         // install new app version targeting 25
         final ApplicationInfo legacy = new ApplicationInfo();
@@ -1338,7 +1341,7 @@
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup("garbage");
         try {
-            mHelper.createNotificationChannel(PKG, UID, channel1, true);
+            mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
             fail("Created a channel with a bad group");
         } catch (IllegalArgumentException e) {
         }
@@ -1351,7 +1354,7 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         assertEquals(ncg.getId(),
                 mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
@@ -1369,20 +1372,20 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
         NotificationChannel channel1a =
                 new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1a.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1a, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
 
         NotificationChannel channel2 =
                 new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel2.setGroup(ncg2.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
 
         NotificationChannel channel3 =
                 new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         List<NotificationChannelGroup> actual =
                 mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
@@ -1416,7 +1419,7 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
         mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
 
         channel1.setImportance(IMPORTANCE_LOW);
@@ -1436,12 +1439,12 @@
     @Test
     public void testCreateChannel_updateName() throws Exception {
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
         NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("hello", actual.getName());
 
         nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
 
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("goodbye", actual.getName());
@@ -1455,13 +1458,13 @@
         NotificationChannelGroup group = new NotificationChannelGroup("group", "");
         mHelper.createNotificationChannelGroup(PKG, UID, group, true);
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
         NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertNull(actual.getGroup());
 
         nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
         nc.setGroup(group.getId());
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
 
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertNotNull(actual.getGroup());
@@ -1486,7 +1489,7 @@
             int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
             for (int j = 0; j < numChannels; j++) {
                 mHelper.createNotificationChannel(pkgName, UID,
-                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
+                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
             }
             expectedChannels.put(pkgName, numChannels);
         }
@@ -1621,10 +1624,10 @@
         c.setGroup(group.getId());
         NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
 
-        mHelper.createNotificationChannel(PKG, UID, a, true);
-        mHelper.createNotificationChannel(PKG, UID, b, true);
-        mHelper.createNotificationChannel(PKG, UID, c, true);
-        mHelper.createNotificationChannel(PKG, UID, d, true);
+        mHelper.createNotificationChannel(PKG, UID, a, true, false);
+        mHelper.createNotificationChannel(PKG, UID, b, true, false);
+        mHelper.createNotificationChannel(PKG, UID, c, true, false);
+        mHelper.createNotificationChannel(PKG, UID, d, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, c.getId());
 
         NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
@@ -1641,22 +1644,31 @@
 
     @Test
     public void testAndroidPkgCanBypassDnd_creation() {
-
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         test.setBypassDnd(true);
 
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
 
         assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                 .canBypassDnd());
     }
 
     @Test
+    public void testDndPkgCanBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+    }
+
+    @Test
     public void testNormalPkgCannotBypassDnd_creation() {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         test.setBypassDnd(true);
 
-        mHelper.createNotificationChannel(PKG, 1000, test, true);
+        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
 
         assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
     }
@@ -1664,11 +1676,11 @@
     @Test
     public void testAndroidPkgCanBypassDnd_update() throws Exception {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
 
         NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         update.setBypassDnd(true);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
 
         assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                 .canBypassDnd());
@@ -1678,12 +1690,24 @@
     }
 
     @Test
-    public void testNormalPkgCannotBypassDnd_update() {
+    public void testDndPkgCanBypassDnd_update() throws Exception {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, 1000, test, true);
+        mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
         NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         update.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG, 1000, update, true);
+        mHelper.createNotificationChannel(PKG, UID, update, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_update() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG, 1000, update, true, false);
         assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
     }
 }